/* * Copyright (c) 2008- Patrick Collison * Copyright (c) 2006- Facebook * * 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. */ #include #include #include #include #include #include #include #include #include #include #include "thrift/platform.h" #include "t_oop_generator.h" using namespace std; /** * Common Lisp code generator. * * @author Patrick Collison */ class t_cl_generator : public t_oop_generator { public: t_cl_generator( t_program* program, const std::map& parsed_options, const std::string& option_string) : t_oop_generator(program) { no_asd = false; system_prefix = "thrift-gen-"; std::map::const_iterator iter; for(iter = parsed_options.begin(); iter != parsed_options.end(); ++iter) { if(iter->first.compare("no_asd") == 0) { no_asd = true; } else if (iter->first.compare("sys_pref") == 0) { system_prefix = iter->second; } else { throw "unknown option cl:" + iter->first; } } out_dir_base_ = "gen-cl"; copy_options_ = option_string; } void init_generator() override; void close_generator() override; std::string display_name() const override; void generate_typedef (t_typedef* ttypedef) override; void generate_enum (t_enum* tenum) override; void generate_const (t_const* tconst) override; void generate_struct (t_struct* tstruct) override; void generate_xception (t_struct* txception) override; void generate_service (t_service* tservice) override; void generate_cl_struct (std::ostream& out, t_struct* tstruct, bool is_exception); void generate_cl_struct_internal (std::ostream& out, t_struct* tstruct, bool is_exception); void generate_exception_sig(std::ostream& out, t_function* f); std::string render_const_value(t_type* type, t_const_value* value); std::string cl_autogen_comment(); void asdf_def(std::ostream &out); void package_def(std::ostream &out); void package_in(std::ostream &out); std::string generated_package(); std::string prefix(std::string name); std::string package_of(t_program* program); std::string package(); std::string render_includes(); std::string type_name(t_type* ttype); std::string typespec (t_type *t); std::string function_signature(t_function* tfunction); std::string argument_list(t_struct* tstruct); std::string cl_docstring(std::string raw); private: int temporary_var; /** * Isolate the variable definitions, as they can require structure definitions */ ofstream_with_content_based_conditional_update f_asd_; ofstream_with_content_based_conditional_update f_types_; ofstream_with_content_based_conditional_update f_vars_; std::string copy_options_; bool no_asd; std::string system_prefix; }; void t_cl_generator::init_generator() { MKDIR(get_out_dir().c_str()); string program_dir = get_out_dir() + "/" + program_name_; MKDIR(program_dir.c_str()); temporary_var = 0; string f_types_name = program_dir + "/" + program_name_ + "-types.lisp"; string f_vars_name = program_dir + "/" + program_name_ + "-vars.lisp"; f_types_.open(f_types_name); f_types_ << cl_autogen_comment() << endl; f_vars_.open(f_vars_name); f_vars_ << cl_autogen_comment() << endl; package_def(f_types_); package_in(f_types_); package_in(f_vars_); if (!no_asd) { string f_asd_name = program_dir + "/" + system_prefix + program_name_ + ".asd"; f_asd_.open(f_asd_name); f_asd_ << cl_autogen_comment() << endl; asdf_def(f_asd_); } } /** * Renders all the imports necessary for including another Thrift program */ string t_cl_generator::render_includes() { const vector& includes = program_->get_includes(); string result = ""; result += ":depends-on (:thrift"; for (auto include : includes) { result += " :" + system_prefix + underscore(include->get_name()); } result += ")\n"; return result; } string t_cl_generator::package_of(t_program* program) { string prefix = program->get_namespace("cl"); return prefix.empty() ? "thrift-generated" : prefix; } string t_cl_generator::package() { return package_of(program_); } string t_cl_generator::prefix(string symbol) { return "\"" + symbol + "\""; } string t_cl_generator::cl_autogen_comment() { return std::string(";;; ") + "Autogenerated by Thrift\n" + ";;; DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING\n" + ";;; options string: " + copy_options_ + "\n"; } string t_cl_generator::cl_docstring(string raw) { replace(raw.begin(), raw.end(), '"', '\''); return raw; } void t_cl_generator::close_generator() { f_asd_.close(); f_types_.close(); f_vars_.close(); } string t_cl_generator::generated_package() { return program_->get_namespace("cpp"); } void t_cl_generator::asdf_def(std::ostream &out) { out << "(asdf:defsystem #:" << system_prefix << program_name_ << endl; indent_up(); out << indent() << render_includes() << indent() << ":serial t" << endl << indent() << ":components (" << "(:file \"" << program_name_ << "-types\") " << "(:file \"" << program_name_ << "-vars\")))" << endl; indent_down(); } /*** * Generate a package definition. Add use references equivalent to the idl file's include statements. */ void t_cl_generator::package_def(std::ostream &out) { const vector& includes = program_->get_includes(); out << "(thrift:def-package :" << package(); if ( includes.size() > 0 ) { out << " :use ("; for (auto include : includes) { out << " :" << include->get_name(); } out << ")"; } out << ")" << endl << endl; } void t_cl_generator::package_in(std::ostream &out) { out << "(cl:in-package :" << package() << ")" << endl << endl; } /** * Generates a typedef. This is not done in Common Lisp, types are all implicit. * * @param ttypedef The type definition */ void t_cl_generator::generate_typedef(t_typedef* ttypedef) { (void)ttypedef; } void t_cl_generator::generate_enum(t_enum* tenum) { f_types_ << "(thrift:def-enum " << prefix(tenum->get_name()) << endl; vector constants = tenum->get_constants(); vector::iterator c_iter; int value = -1; indent_up(); f_types_ << indent() << "("; for (c_iter = constants.begin(); c_iter != constants.end(); ++c_iter) { value = (*c_iter)->get_value(); if(c_iter != constants.begin()) f_types_ << endl << indent() << " "; f_types_ << "(\"" << (*c_iter)->get_name() << "\" . " << value << ")"; } indent_down(); f_types_ << "))" << endl << endl; } /** * Generate a constant value */ void t_cl_generator::generate_const(t_const* tconst) { t_type* type = tconst->get_type(); string name = tconst->get_name(); t_const_value* value = tconst->get_value(); f_vars_ << "(thrift:def-constant " << prefix(name) << " " << render_const_value(type, value) << ")" << endl << endl; } /** * Prints the value of a constant with the given type. Note that type checking * is NOT performed in this function as it is always run beforehand using the * validate_types method in main.cc */ string t_cl_generator::render_const_value(t_type* type, t_const_value* value) { type = get_true_type(type); std::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 << "\"" << value->get_string() << "\""; break; case t_base_type::TYPE_BOOL: out << (value->get_integer() > 0 ? "t" : "nil"); break; case t_base_type::TYPE_I8: case t_base_type::TYPE_I16: case t_base_type::TYPE_I32: case t_base_type::TYPE_I64: out << value->get_integer(); 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()) { indent(out) << value->get_integer(); } else if (type->is_struct() || type->is_xception()) { out << (type->is_struct() ? "(make-instance '" : "(make-exception '") << lowercase(type->get_name()) << " " << endl; indent_up(); const vector& fields = ((t_struct*)type)->get_members(); vector::const_iterator f_iter; const map& val = value->get_map(); map::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(); } out << indent() << ":" << v_iter->first->get_string() << " " << render_const_value(field_type, v_iter->second) << endl; } out << indent() << ")"; indent_down(); } else if (type->is_map()) { // emit an hash form with both keys and values to be evaluated t_type* ktype = ((t_map*)type)->get_key_type(); t_type* vtype = ((t_map*)type)->get_val_type(); out << "(thrift:map "; indent_up(); const map& val = value->get_map(); map::const_iterator v_iter; for (v_iter = val.begin(); v_iter != val.end(); ++v_iter) { out << endl << indent() << "(cl:cons " << render_const_value(ktype, v_iter->first) << " " << render_const_value(vtype, v_iter->second) << ")"; } indent_down(); out << indent() << ")"; } else if (type->is_list() || type->is_set()) { t_type* etype; if (type->is_list()) { etype = ((t_list*)type)->get_elem_type(); } else { etype = ((t_set*)type)->get_elem_type(); } if (type->is_set()) { out << "(thrift:set" << endl; } else { out << "(thrift:list" << endl; } indent_up(); indent_up(); const vector& val = value->get_list(); vector::const_iterator v_iter; for (v_iter = val.begin(); v_iter != val.end(); ++v_iter) { out << indent() << render_const_value(etype, *v_iter) << endl; } out << indent() << ")"; indent_down(); indent_down(); } else { throw "CANNOT GENERATE CONSTANT FOR TYPE: " + type->get_name(); } return out.str(); } void t_cl_generator::generate_struct(t_struct* tstruct) { generate_cl_struct(f_types_, tstruct, false); } void t_cl_generator::generate_xception(t_struct* txception) { generate_cl_struct(f_types_, txception, true); } void t_cl_generator::generate_cl_struct_internal(std::ostream& out, t_struct* tstruct, bool is_exception) { (void)is_exception; const vector& members = tstruct->get_members(); vector::const_iterator m_iter; out << "("; for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) { t_const_value* value = (*m_iter)->get_value(); t_type* type = (*m_iter)->get_type(); if (m_iter != members.begin()) { out << endl << indent() << " "; } out << "(" << prefix((*m_iter)->get_name()) << " " << ( (nullptr != value) ? render_const_value(type, value) : "nil" ) << " :id " << (*m_iter)->get_key(); if ( type->is_base_type() && "string" == typespec(type) ) if ( ((t_base_type*)type)->is_binary() ) out << " :type binary"; else out << " :type string"; else out << " :type " << typespec(type); if ( (*m_iter)->get_req() == t_field::T_OPTIONAL ) { out << " :optional t"; } if ( (*m_iter)->has_doc()) { out << " :documentation \"" << cl_docstring((*m_iter)->get_doc()) << "\""; } out <<")"; } out << ")"; } void t_cl_generator::generate_cl_struct(std::ostream& out, t_struct* tstruct, bool is_exception = false) { std::string name = type_name(tstruct); out << (is_exception ? "(thrift:def-exception " : "(thrift:def-struct ") << prefix(name) << endl; indent_up(); if ( tstruct->has_doc() ) { out << indent() ; out << "\"" << cl_docstring(tstruct->get_doc()) << "\"" << endl; } out << indent() ; generate_cl_struct_internal(out, tstruct, is_exception); indent_down(); out << ")" << endl << endl; } void t_cl_generator::generate_exception_sig(std::ostream& out, t_function* f) { generate_cl_struct_internal(out, f->get_xceptions(), true); } void t_cl_generator::generate_service(t_service* tservice) { string extends_client; vector functions = tservice->get_functions(); vector::iterator f_iter; if (tservice->get_extends() != nullptr) { extends_client = type_name(tservice->get_extends()); } extends_client = extends_client.empty() ? "nil" : prefix(extends_client); f_types_ << "(thrift:def-service " << prefix(service_name_) << " " << extends_client; indent_up(); if ( tservice->has_doc()) { f_types_ << endl << indent() << "(:documentation \"" << cl_docstring(tservice->get_doc()) << "\")"; } for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) { t_function* function = *f_iter; string fname = function->get_name(); string signature = function_signature(function); t_struct* exceptions = function->get_xceptions(); const vector& xmembers = exceptions->get_members(); f_types_ << endl << indent() << "(:method " << prefix(fname); f_types_ << " (" << signature << " " << typespec((*f_iter)->get_returntype()) << ")"; if (xmembers.size() > 0) { f_types_ << endl << indent() << " :exceptions " ; generate_exception_sig(f_types_, function); } if ( (*f_iter)->is_oneway() ) { f_types_ << endl << indent() << " :oneway t"; } if ( (*f_iter)->has_doc() ) { f_types_ << endl << indent() << " :documentation \"" << cl_docstring((*f_iter)->get_doc()) << "\""; } f_types_ << ")"; } f_types_ << ")" << endl << endl; indent_down(); } string t_cl_generator::typespec(t_type *t) { t = get_true_type(t); if (t -> is_binary()){ return "binary"; } else if (t->is_base_type()) { return type_name(t); } else if (t->is_map()) { t_map *m = (t_map*) t; return "(thrift:map " + typespec(m->get_key_type()) + " " + typespec(m->get_val_type()) + ")"; } else if (t->is_struct() || t->is_xception()) { return "(struct " + prefix(type_name(t)) + ")"; } else if (t->is_list()) { return "(thrift:list " + typespec(((t_list*) t)->get_elem_type()) + ")"; } else if (t->is_set()) { return "(thrift:set " + typespec(((t_set*) t)->get_elem_type()) + ")"; } else if (t->is_enum()) { return "(enum \"" + ((t_enum*) t)->get_name() + "\")"; } else { throw "Sorry, I don't know how to generate this: " + type_name(t); } } string t_cl_generator::function_signature(t_function* tfunction) { return argument_list(tfunction->get_arglist()); } string t_cl_generator::argument_list(t_struct* tstruct) { stringstream res; res << "("; const vector& fields = tstruct->get_members(); vector::const_iterator f_iter; bool first = true; for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { if (first) { first = false; } else { res << " "; } res << "(" + prefix((*f_iter)->get_name()) << " " << typespec((*f_iter)->get_type()) << " " << (*f_iter)->get_key() << ")"; } res << ")"; return res.str(); } string t_cl_generator::type_name(t_type* ttype) { string prefix = ""; t_program* program = ttype->get_program(); if (program != nullptr && program != program_) prefix = package_of(program) == package() ? "" : package_of(program) + ":"; string name = ttype->get_name(); if (ttype->is_struct() || ttype->is_xception()) name = lowercase(ttype->get_name()); return prefix + name; } std::string t_cl_generator::display_name() const { return "Common Lisp"; } THRIFT_REGISTER_GENERATOR( cl, "Common Lisp", " no_asd: Do not define ASDF systems for each generated Thrift program.\n" " sys_pref= The prefix to give ASDF system names. Default: thrift-gen-\n")