| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| * |
| * Contains some contributions under the Thrift Software License. |
| * Please see doc/old-thrift-license.txt in the Thrift distribution for |
| * details. |
| */ |
| |
| #include <cassert> |
| |
| #include <fstream> |
| #include <iostream> |
| #include <set> |
| #include <sstream> |
| #include <string> |
| #include <vector> |
| |
| #include <sys/stat.h> |
| |
| #include "thrift/platform.h" |
| #include "thrift/generate/t_oop_generator.h" |
| |
| using std::map; |
| using std::ofstream; |
| using std::ostream; |
| using std::ostringstream; |
| using std::set; |
| using std::string; |
| using std::vector; |
| |
| /** |
| * D code generator. |
| * |
| * generate_*() functions are called by the base class to emit code for the |
| * given entity, print_*() functions write a piece of code to the passed |
| * stream, and render_*() return a string containing the D representation of |
| * the passed entity. |
| */ |
| class t_d_generator : public t_oop_generator { |
| public: |
| t_d_generator(t_program* program, |
| const std::map<string, string>& parsed_options, |
| const string& option_string) |
| : t_oop_generator(program) { |
| (void)option_string; |
| std::map<std::string, std::string>::const_iterator iter; |
| |
| /* no options yet */ |
| for( iter = parsed_options.begin(); iter != parsed_options.end(); ++iter) { |
| throw "unknown option d:" + iter->first; |
| } |
| |
| out_dir_base_ = "gen-d"; |
| } |
| std::string display_name() const override; |
| |
| protected: |
| |
| // D reserved words are suffixed with an underscore |
| static string suffix_if_reserved(const string& name) { |
| const bool isIn = std::binary_search(std::begin(d_reserved_words), std::end(d_reserved_words), name); |
| string ret = isIn ? name + "_" : name; |
| return ret; |
| } |
| |
| void init_generator() override { |
| // Make output directory |
| MKDIR(get_out_dir().c_str()); |
| |
| string dir = program_->get_namespace("d"); |
| string subdir = get_out_dir(); |
| string::size_type loc; |
| while ((loc = dir.find(".")) != string::npos) { |
| subdir = subdir + "/" + dir.substr(0, loc); |
| MKDIR(subdir.c_str()); |
| dir = dir.substr(loc + 1); |
| } |
| if (!dir.empty()) { |
| subdir = subdir + "/" + dir; |
| MKDIR(subdir.c_str()); |
| } |
| |
| package_dir_ = subdir + "/"; |
| |
| // Make output file |
| string f_types_name = package_dir_ + program_name_ + "_types.d"; |
| f_types_.open(f_types_name.c_str()); |
| |
| // Print header |
| f_types_ << autogen_comment() << "module " << render_package(*program_) << program_name_ |
| << "_types;" << '\n' << '\n'; |
| |
| print_default_imports(f_types_); |
| |
| // Include type modules from other imported programs. |
| const vector<t_program*>& includes = program_->get_includes(); |
| for (auto include : includes) { |
| f_types_ << "public import " << render_package(*include) << include->get_name() |
| << "_types;" << '\n'; |
| } |
| if (!includes.empty()) |
| f_types_ << '\n'; |
| } |
| |
| void close_generator() override { |
| // Close output file |
| f_types_.close(); |
| } |
| |
| void generate_consts(std::vector<t_const*> consts) override { |
| if (!consts.empty()) { |
| string f_consts_name = package_dir_ + program_name_ + "_constants.d"; |
| ofstream_with_content_based_conditional_update f_consts; |
| f_consts.open(f_consts_name.c_str()); |
| |
| f_consts << autogen_comment() << "module " << render_package(*program_) << program_name_ |
| << "_constants;" << '\n' << '\n'; |
| |
| print_default_imports(f_consts); |
| |
| f_consts << "import " << render_package(*get_program()) << program_name_ << "_types;" << '\n' |
| << '\n'; |
| |
| vector<t_const*>::iterator c_iter; |
| for (c_iter = consts.begin(); c_iter != consts.end(); ++c_iter) { |
| this->emit_doc(*c_iter, f_consts); |
| string name = suffix_if_reserved((*c_iter)->get_name()); |
| t_type* type = (*c_iter)->get_type(); |
| indent(f_consts) << "immutable(" << render_type_name(type) << ") " << name << ";" << '\n'; |
| } |
| |
| f_consts << '\n' << "shared static this() {" << '\n'; |
| indent_up(); |
| |
| bool first = true; |
| for (c_iter = consts.begin(); c_iter != consts.end(); ++c_iter) { |
| if (first) { |
| first = false; |
| } else { |
| f_consts << '\n'; |
| } |
| t_type* type = (*c_iter)->get_type(); |
| indent(f_consts) << suffix_if_reserved((*c_iter)->get_name()) << " = "; |
| if (!is_immutable_type(type)) { |
| f_consts << "cast(immutable(" << render_type_name(type) << ")) "; |
| } |
| f_consts << render_const_value(type, (*c_iter)->get_value()) << ";" << '\n'; |
| } |
| indent_down(); |
| indent(f_consts) << "}" << '\n'; |
| } |
| } |
| |
| void generate_typedef(t_typedef* ttypedef) override { |
| this->emit_doc(ttypedef, f_types_); |
| f_types_ << indent() << "alias " << render_type_name(ttypedef->get_type()) << " " |
| << ttypedef->get_symbolic() << ";" << '\n' << '\n'; |
| } |
| |
| void generate_enum(t_enum* tenum) override { |
| vector<t_enum_value*> constants = tenum->get_constants(); |
| |
| this->emit_doc(tenum, f_types_); |
| string enum_name = suffix_if_reserved(tenum->get_name()); |
| f_types_ << indent() << "enum " << enum_name << " {" << '\n'; |
| |
| indent_up(); |
| |
| vector<t_enum_value*>::const_iterator c_iter; |
| for (c_iter = constants.begin(); c_iter != constants.end(); ++c_iter) { |
| this->emit_doc(*c_iter, f_types_); |
| indent(f_types_) << suffix_if_reserved((*c_iter)->get_name()); |
| f_types_ << " = " << (*c_iter)->get_value() << ","; |
| } |
| |
| f_types_ << '\n'; |
| indent_down(); |
| indent(f_types_) << "}" << '\n'; |
| |
| f_types_ << '\n'; |
| } |
| |
| void generate_struct(t_struct* tstruct) override { |
| print_struct_definition(f_types_, tstruct, false); |
| } |
| |
| void generate_xception(t_struct* txception) override { |
| print_struct_definition(f_types_, txception, true); |
| } |
| |
| void generate_service(t_service* tservice) override { |
| string svc_name = suffix_if_reserved(tservice->get_name()); |
| |
| // Service implementation file includes |
| string f_servicename = package_dir_ + svc_name + ".d"; |
| ofstream_with_content_based_conditional_update f_service; |
| f_service.open(f_servicename.c_str()); |
| f_service << autogen_comment() << "module " << suffix_if_reserved(render_package(*program_)) << svc_name << ";" |
| << '\n' << '\n'; |
| |
| print_default_imports(f_service); |
| |
| f_service << "import " << suffix_if_reserved(render_package(*get_program())) << program_name_ << "_types;" << '\n'; |
| |
| t_service* extends_service = tservice->get_extends(); |
| if (extends_service != nullptr) { |
| f_service << "import " << suffix_if_reserved(render_package(*(extends_service->get_program()))) |
| << suffix_if_reserved(extends_service->get_name()) << ";" << '\n'; |
| } |
| |
| f_service << '\n'; |
| |
| string extends = ""; |
| if (tservice->get_extends() != nullptr) { |
| extends = " : " + suffix_if_reserved(render_type_name(tservice->get_extends())); |
| } |
| |
| this->emit_doc(tservice, f_service); |
| f_service << indent() << "interface " << svc_name << extends << " {" << '\n'; |
| indent_up(); |
| |
| // Collect all the exception types service methods can throw so we can |
| // emit the necessary aliases later. |
| set<t_type*> exception_types; |
| |
| // Print the method signatures. |
| vector<t_function*> functions = tservice->get_functions(); |
| vector<t_function*>::iterator fn_iter; |
| for (fn_iter = functions.begin(); fn_iter != functions.end(); ++fn_iter) { |
| this->emit_doc(*fn_iter, f_service); |
| f_service << indent(); |
| print_function_signature(f_service, *fn_iter); |
| f_service << ";" << '\n'; |
| |
| const vector<t_field*>& exceptions = (*fn_iter)->get_xceptions()->get_members(); |
| vector<t_field*>::const_iterator ex_iter; |
| for (ex_iter = exceptions.begin(); ex_iter != exceptions.end(); ++ex_iter) { |
| exception_types.insert((*ex_iter)->get_type()); |
| } |
| } |
| |
| // Alias the exception types into the current scope. |
| if (!exception_types.empty()) |
| f_service << '\n'; |
| set<t_type*>::const_iterator et_iter; |
| for (et_iter = exception_types.begin(); et_iter != exception_types.end(); ++et_iter) { |
| indent(f_service) << "alias " << render_package(*(*et_iter)->get_program()) |
| << (*et_iter)->get_program()->get_name() << "_types" |
| << "." << (*et_iter)->get_name() << " " << (*et_iter)->get_name() << ";" |
| << '\n'; |
| } |
| |
| // Write the method metadata. |
| ostringstream meta; |
| indent_up(); |
| bool first = true; |
| for (fn_iter = functions.begin(); fn_iter != functions.end(); ++fn_iter) { |
| if ((*fn_iter)->get_arglist()->get_members().empty() |
| && (*fn_iter)->get_xceptions()->get_members().empty() && !(*fn_iter)->is_oneway()) { |
| continue; |
| } |
| |
| if (first) { |
| first = false; |
| } else { |
| meta << ","; |
| } |
| |
| meta << '\n' << indent() << "TMethodMeta(`" << suffix_if_reserved((*fn_iter)->get_name()) << "`, " << '\n'; |
| indent_up(); |
| indent(meta) << "["; |
| |
| bool first = true; |
| const vector<t_field*>& params = (*fn_iter)->get_arglist()->get_members(); |
| vector<t_field*>::const_iterator p_iter; |
| for (p_iter = params.begin(); p_iter != params.end(); ++p_iter) { |
| if (first) { |
| first = false; |
| } else { |
| meta << ", "; |
| } |
| |
| meta << "TParamMeta(`" << suffix_if_reserved((*p_iter)->get_name()) << "`, " << (*p_iter)->get_key(); |
| |
| t_const_value* cv = (*p_iter)->get_value(); |
| if (cv != nullptr) { |
| meta << ", q{" << render_const_value((*p_iter)->get_type(), cv) << "}"; |
| } |
| meta << ")"; |
| } |
| |
| meta << "]"; |
| |
| if (!(*fn_iter)->get_xceptions()->get_members().empty() || (*fn_iter)->is_oneway()) { |
| meta << "," << '\n' << indent() << "["; |
| |
| bool first = true; |
| const vector<t_field*>& exceptions = (*fn_iter)->get_xceptions()->get_members(); |
| vector<t_field*>::const_iterator ex_iter; |
| for (ex_iter = exceptions.begin(); ex_iter != exceptions.end(); ++ex_iter) { |
| if (first) { |
| first = false; |
| } else { |
| meta << ", "; |
| } |
| |
| meta << "TExceptionMeta(`" << suffix_if_reserved((*ex_iter)->get_name()) << "`, " |
| << (*ex_iter)->get_key() << ", `" << (*ex_iter)->get_type()->get_name() << "`)"; |
| } |
| |
| meta << "]"; |
| } |
| |
| if ((*fn_iter)->is_oneway()) { |
| meta << "," << '\n' << indent() << "TMethodType.ONEWAY"; |
| } |
| |
| indent_down(); |
| meta << '\n' << indent() << ")"; |
| } |
| indent_down(); |
| |
| string meta_str(meta.str()); |
| if (!meta_str.empty()) { |
| f_service << '\n' << indent() << "enum methodMeta = [" << meta_str << '\n' << indent() << "];" |
| << '\n'; |
| } |
| |
| indent_down(); |
| indent(f_service) << "}" << '\n'; |
| |
| // Server skeleton generation. |
| string f_skeletonname = package_dir_ + svc_name + "_server.skeleton.d"; |
| ofstream_with_content_based_conditional_update f_skeleton; |
| f_skeleton.open(f_skeletonname.c_str()); |
| print_server_skeleton(f_skeleton, tservice); |
| f_skeleton.close(); |
| } |
| |
| void emit_doc(t_doc *doc, std::ostream& out) { |
| if (!doc->has_doc()) { |
| return; |
| } |
| indent(out) << "/**" << '\n'; |
| indent_up(); |
| // No line break -- comments reliably have a newline at the end. |
| // This is true even for stuff like: |
| // /** method infos */ void foo(/** huh?*/ 1: i64 stuff) |
| indent(out) << doc->get_doc(); |
| indent_down(); |
| indent(out) << "*/" << '\n'; |
| } |
| |
| private: |
| /** |
| * Writes a server skeleton for the passed service to out. |
| */ |
| |
| void print_server_skeleton(ostream& out, t_service* tservice) { |
| string svc_name = suffix_if_reserved(tservice->get_name()); |
| |
| out << "/*" << '\n' |
| << " * This auto-generated skeleton file illustrates how to build a server. If you" << '\n' |
| << " * intend to customize it, you should edit a copy with another file name to " << '\n' |
| << " * avoid overwriting it when running the generator again." << '\n' << " */" << '\n' |
| << "module " << render_package(*tservice->get_program()) << svc_name << "_server;" << '\n' |
| << '\n' << "import std.stdio;" << '\n' << "import thrift.codegen.processor;" << '\n' |
| << "import thrift.protocol.binary;" << '\n' << "import thrift.server.simple;" << '\n' |
| << "import thrift.server.transport.socket;" << '\n' << "import thrift.transport.buffered;" |
| << '\n' << "import thrift.util.hashset;" << '\n' << '\n' << "import " |
| << render_package(*tservice->get_program()) << svc_name << ";" << '\n' << "import " |
| << render_package(*get_program()) << program_name_ << "_types;" << '\n' << '\n' << '\n' |
| << "class " << svc_name << "Handler : " << svc_name << " {" << '\n'; |
| |
| indent_up(); |
| out << indent() << "this() {" << '\n' << indent() << " // Your initialization goes here." |
| << '\n' << indent() << "}" << '\n' << '\n'; |
| |
| vector<t_function*> functions = tservice->get_functions(); |
| vector<t_function*>::iterator f_iter; |
| for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) { |
| out << indent(); |
| print_function_signature(out, *f_iter); |
| out << " {" << '\n'; |
| |
| indent_up(); |
| |
| out << indent() << "// Your implementation goes here." << '\n' << indent() << "writeln(\"" |
| << suffix_if_reserved((*f_iter)->get_name()) << " called\");" << '\n'; |
| |
| t_type* rt = (*f_iter)->get_returntype(); |
| if (!rt->is_void()) { |
| indent(out) << "return typeof(return).init;" << '\n'; |
| } |
| |
| indent_down(); |
| |
| out << indent() << "}" << '\n' << '\n'; |
| } |
| |
| indent_down(); |
| out << "}" << '\n' << '\n'; |
| |
| out << indent() << "void main() {" << '\n'; |
| indent_up(); |
| out << indent() << "auto protocolFactory = new TBinaryProtocolFactory!();" << '\n' << indent() |
| << "auto processor = new TServiceProcessor!" << svc_name << "(new " << svc_name |
| << "Handler);" << '\n' << indent() << "auto serverTransport = new TServerSocket(9090);" |
| << '\n' << indent() << "auto transportFactory = new TBufferedTransportFactory;" << '\n' |
| << indent() << "auto server = new TSimpleServer(" << '\n' << indent() |
| << " processor, serverTransport, transportFactory, protocolFactory);" << '\n' << indent() |
| << "server.serve();" << '\n'; |
| indent_down(); |
| out << "}" << '\n'; |
| } |
| |
| /** |
| * Writes the definition of a struct or an exception type to out. |
| */ |
| |
| void print_struct_definition(ostream& out, t_struct* tstruct, bool is_exception) { |
| const vector<t_field*>& members = tstruct->get_members(); |
| |
| if (is_exception) { |
| indent(out) << "class " << suffix_if_reserved(tstruct->get_name()) << " : TException {" << '\n'; |
| } else { |
| indent(out) << "struct " << suffix_if_reserved(tstruct->get_name()) << " {" << '\n'; |
| } |
| indent_up(); |
| |
| // Declare all fields. |
| vector<t_field*>::const_iterator m_iter; |
| for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) { |
| indent(out) << render_type_name((*m_iter)->get_type()) << " " << suffix_if_reserved((*m_iter)->get_name()) << ";" |
| << '\n'; |
| } |
| |
| if (!members.empty()) |
| indent(out) << '\n'; |
| indent(out) << "mixin TStructHelpers!("; |
| |
| if (!members.empty()) { |
| // If there are any fields, construct the TFieldMeta array to pass to |
| // TStructHelpers. We can't just pass an empty array if not because [] |
| // doesn't pass the TFieldMeta[] constraint. |
| out << "["; |
| indent_up(); |
| |
| bool first = true; |
| vector<t_field*>::const_iterator m_iter; |
| for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) { |
| if (first) { |
| first = false; |
| } else { |
| out << ","; |
| } |
| out << '\n'; |
| |
| indent(out) << "TFieldMeta(`" << suffix_if_reserved((*m_iter)->get_name()) << "`, " << (*m_iter)->get_key(); |
| |
| t_const_value* cv = (*m_iter)->get_value(); |
| t_field::e_req req = (*m_iter)->get_req(); |
| out << ", " << render_req(req); |
| if (cv != nullptr) { |
| out << ", q{" << render_const_value((*m_iter)->get_type(), cv) << "}"; |
| } |
| out << ")"; |
| } |
| |
| indent_down(); |
| out << '\n' << indent() << "]"; |
| } |
| |
| out << ");" << '\n'; |
| |
| indent_down(); |
| indent(out) << "}" << '\n' << '\n'; |
| } |
| |
| /** |
| * Prints the D function signature (including return type) for the given |
| * method. |
| */ |
| |
| void print_function_signature(ostream& out, t_function* fn) { |
| out << render_type_name(fn->get_returntype()) << " " << suffix_if_reserved(fn->get_name()) << "("; |
| |
| const vector<t_field*>& fields = fn->get_arglist()->get_members(); |
| vector<t_field*>::const_iterator f_iter; |
| bool first = true; |
| for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { |
| if (first) { |
| first = false; |
| } else { |
| out << ", "; |
| } |
| out << render_type_name((*f_iter)->get_type(), true) << " " << suffix_if_reserved((*f_iter)->get_name()); |
| } |
| |
| out << ")"; |
| } |
| |
| /** |
| * Returns the D representation of value. The result is guaranteed to be a |
| * single expression; for complex types, immediately called delegate |
| * literals are used to achieve this. |
| */ |
| string render_const_value(t_type* type, t_const_value* value) { |
| // Resolve any typedefs. |
| type = get_true_type(type); |
| |
| ostringstream out; |
| if (type->is_base_type()) { |
| t_base_type::t_base tbase = ((t_base_type*)type)->get_base(); |
| switch (tbase) { |
| case t_base_type::TYPE_STRING: |
| out << '"' << get_escaped_string(value) << '"'; |
| break; |
| case t_base_type::TYPE_BOOL: |
| out << ((value->get_integer() > 0) ? "true" : "false"); |
| break; |
| case t_base_type::TYPE_I8: |
| case t_base_type::TYPE_I16: |
| out << "cast(" << render_type_name(type) << ")" << value->get_integer(); |
| break; |
| case t_base_type::TYPE_I32: |
| out << value->get_integer(); |
| break; |
| case t_base_type::TYPE_I64: |
| out << value->get_integer() << "L"; |
| break; |
| case t_base_type::TYPE_DOUBLE: |
| if (value->get_type() == t_const_value::CV_INTEGER) { |
| out << value->get_integer(); |
| } else { |
| out << value->get_double(); |
| } |
| break; |
| default: |
| throw "Compiler error: No const of base type " + t_base_type::t_base_name(tbase); |
| } |
| } else if (type->is_enum()) { |
| out << "cast(" << render_type_name(type) << ")" << value->get_integer(); |
| } else { |
| out << "{" << '\n'; |
| indent_up(); |
| |
| indent(out) << render_type_name(type) << " v;" << '\n'; |
| if (type->is_struct() || type->is_xception()) { |
| indent(out) << "v = " << (type->is_xception() ? "new " : "") << render_type_name(type) |
| << "();" << '\n'; |
| |
| const vector<t_field*>& fields = ((t_struct*)type)->get_members(); |
| vector<t_field*>::const_iterator f_iter; |
| const map<t_const_value*, t_const_value*, t_const_value::value_compare>& val = value->get_map(); |
| map<t_const_value*, t_const_value*, t_const_value::value_compare>::const_iterator v_iter; |
| for (v_iter = val.begin(); v_iter != val.end(); ++v_iter) { |
| t_type* field_type = nullptr; |
| for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { |
| if ((*f_iter)->get_name() == v_iter->first->get_string()) { |
| field_type = (*f_iter)->get_type(); |
| } |
| } |
| if (field_type == nullptr) { |
| throw "Type error: " + type->get_name() + " has no field " |
| + v_iter->first->get_string(); |
| } |
| string val = render_const_value(field_type, v_iter->second); |
| indent(out) << "v.set!`" << v_iter->first->get_string() << "`(" << val << ");" << '\n'; |
| } |
| } else if (type->is_map()) { |
| t_type* ktype = ((t_map*)type)->get_key_type(); |
| t_type* vtype = ((t_map*)type)->get_val_type(); |
| const map<t_const_value*, t_const_value*, t_const_value::value_compare>& val = value->get_map(); |
| map<t_const_value*, t_const_value*, t_const_value::value_compare>::const_iterator v_iter; |
| for (v_iter = val.begin(); v_iter != val.end(); ++v_iter) { |
| string key = render_const_value(ktype, v_iter->first); |
| string val = render_const_value(vtype, v_iter->second); |
| indent(out) << "v["; |
| if (!is_immutable_type(ktype)) { |
| out << "cast(immutable(" << render_type_name(ktype) << "))"; |
| } |
| out << key << "] = " << val << ";" << '\n'; |
| } |
| } else if (type->is_list()) { |
| t_type* etype = ((t_list*)type)->get_elem_type(); |
| const vector<t_const_value*>& val = value->get_list(); |
| vector<t_const_value*>::const_iterator v_iter; |
| for (v_iter = val.begin(); v_iter != val.end(); ++v_iter) { |
| string val = render_const_value(etype, *v_iter); |
| indent(out) << "v ~= " << val << ";" << '\n'; |
| } |
| } else if (type->is_set()) { |
| t_type* etype = ((t_set*)type)->get_elem_type(); |
| const vector<t_const_value*>& val = value->get_list(); |
| vector<t_const_value*>::const_iterator v_iter; |
| for (v_iter = val.begin(); v_iter != val.end(); ++v_iter) { |
| string val = render_const_value(etype, *v_iter); |
| indent(out) << "v ~= " << val << ";" << '\n'; |
| } |
| } else { |
| throw "Compiler error: Invalid type in render_const_value: " + type->get_name(); |
| } |
| indent(out) << "return v;" << '\n'; |
| |
| indent_down(); |
| indent(out) << "}()"; |
| } |
| |
| return out.str(); |
| } |
| |
| /** |
| * Returns the D package to which modules for program are written (with a |
| * trailing dot, if not empty). |
| */ |
| string render_package(const t_program& program) const { |
| string package = program.get_namespace("d"); |
| if (package.size() == 0) |
| return ""; |
| return package + "."; |
| } |
| |
| /** |
| * Returns the name of the D repesentation of ttype. |
| * |
| * If isArg is true, a const reference to the type will be returned for |
| * structs. |
| */ |
| string render_type_name(const t_type* ttype, bool isArg = false) const { |
| if (ttype->is_base_type()) { |
| t_base_type::t_base tbase = ((t_base_type*)ttype)->get_base(); |
| switch (tbase) { |
| case t_base_type::TYPE_VOID: |
| return "void"; |
| case t_base_type::TYPE_STRING: |
| return "string"; |
| case t_base_type::TYPE_BOOL: |
| return "bool"; |
| case t_base_type::TYPE_I8: |
| return "byte"; |
| case t_base_type::TYPE_I16: |
| return "short"; |
| case t_base_type::TYPE_I32: |
| return "int"; |
| case t_base_type::TYPE_I64: |
| return "long"; |
| case t_base_type::TYPE_DOUBLE: |
| return "double"; |
| default: |
| throw "Compiler error: No D type name for base type " + t_base_type::t_base_name(tbase); |
| } |
| } |
| |
| if (ttype->is_container()) { |
| t_container* tcontainer = (t_container*)ttype; |
| if (tcontainer->has_cpp_name()) { |
| return tcontainer->get_cpp_name(); |
| } else if (ttype->is_map()) { |
| t_map* tmap = (t_map*)ttype; |
| t_type* ktype = tmap->get_key_type(); |
| |
| string name = render_type_name(tmap->get_val_type()) + "["; |
| if (!is_immutable_type(ktype)) { |
| name += "immutable("; |
| } |
| name += render_type_name(ktype); |
| if (!is_immutable_type(ktype)) { |
| name += ")"; |
| } |
| name += "]"; |
| return name; |
| } else if (ttype->is_set()) { |
| t_set* tset = (t_set*)ttype; |
| return "HashSet!(" + render_type_name(tset->get_elem_type()) + ")"; |
| } else if (ttype->is_list()) { |
| t_list* tlist = (t_list*)ttype; |
| return render_type_name(tlist->get_elem_type()) + "[]"; |
| } |
| } |
| |
| if (ttype->is_struct() && isArg) { |
| return "ref const(" + ttype->get_name() + ")"; |
| } else { |
| return ttype->get_name(); |
| } |
| } |
| |
| /** |
| * Returns the D TReq enum member corresponding to req. |
| */ |
| string render_req(t_field::e_req req) const { |
| switch (req) { |
| case t_field::T_OPT_IN_REQ_OUT: |
| return "TReq.OPT_IN_REQ_OUT"; |
| case t_field::T_OPTIONAL: |
| return "TReq.OPTIONAL"; |
| case t_field::T_REQUIRED: |
| return "TReq.REQUIRED"; |
| default: { |
| std::stringstream ss; |
| ss << "Compiler error: Invalid requirement level " << req; |
| throw ss.str(); |
| } |
| } |
| } |
| |
| /** |
| * Writes the default list of imports (which are written to every generated |
| * module) to f. |
| */ |
| |
| void print_default_imports(ostream& out) { |
| indent(out) << "import thrift.base;" << '\n' << "import thrift.codegen.base;" << '\n' |
| << "import thrift.util.hashset;" << '\n' << '\n'; |
| } |
| |
| /** |
| * Returns whether type is »intrinsically immutable«, in the sense that |
| * a value of that type is implicitly castable to immutable(type), and it is |
| * allowed for AA keys without an immutable() qualifier. |
| */ |
| bool is_immutable_type(t_type* type) const { |
| t_type* ttype = get_true_type(type); |
| return ttype->is_base_type() || ttype->is_enum(); |
| } |
| |
| /* |
| * File streams, stored here to avoid passing them as parameters to every |
| * function. |
| */ |
| ofstream_with_content_based_conditional_update f_types_; |
| ofstream_with_content_based_conditional_update f_header_; |
| |
| string package_dir_; |
| |
| protected: |
| static vector<string> d_reserved_words; |
| |
| }; |
| |
| vector<string> t_d_generator::d_reserved_words = { |
| // The keywords are extracted from https://dlang.org/spec/lex.html |
| // and sorted for use with std::binary_search |
| "__FILE_FULL_PATH__", "__FILE__", "__FUNCTION__", "__LINE__", "__MODULE__", |
| "__PRETTY_FUNCTION__", "__gshared", "__parameters", "__traits", "__vector", |
| "abstract", "alias", "align", "asm", "assert", "auto", "body", "bool", |
| "break", "byte", "case", "cast", "catch", "cdouble", "cent", "cfloat", |
| "char", "class", "const", "continue", "creal", "dchar", "debug", "default", |
| "delegate", "delete", "deprecated", "do", "double", "else", "enum", |
| "export", "extern", "false", "final", "finally", "float", "for", "foreach", |
| "foreach_reverse", "function", "goto", "idouble", "if", "ifloat", "immutable", |
| "import", "in", "inout", "int", "interface", "invariant", "ireal", "is", |
| "lazy", "long", "macro ", "mixin", "module", "new", "nothrow", "null", "out", |
| "override", "package", "pragma", "private", "protected", "public", "pure", |
| "real", "ref", "return", "scope", "shared", "short", "static", "struct", |
| "super", "switch", "synchronized", "template", "this", "throw", "true", "try", |
| "typeid", "typeof", "ubyte", "ucent", "uint", "ulong", "union", "unittest", |
| "ushort", "version", "void", "wchar", "while", "with" |
| }; |
| |
| std::string t_d_generator::display_name() const { |
| return "D"; |
| } |
| |
| |
| THRIFT_REGISTER_GENERATOR(d, "D", "") |