From 04057ac28a72ad4001def05a7bc8e13cc640b5ca Mon Sep 17 00:00:00 2001 From: Max-Gerd Retzlaff Date: Tue, 23 Aug 2022 17:38:34 +0200 Subject: Revert "THRIFT-5501 Remove Common Lisp support" This reverts commit d88d4f93b3390989bd47a43f3941ca7d576750f6. --- compiler/cpp/CMakeLists.txt | 1 + compiler/cpp/Makefile.am | 1 + compiler/cpp/compiler.vcxproj | 1 + compiler/cpp/src/thrift/generate/t_cl_generator.cc | 558 +++++++++++++++++++++ compiler/cpp/src/thrift/generate/t_generator.cc | 2 +- compiler/cpp/tests/CMakeLists.txt | 1 + 6 files changed, 563 insertions(+), 1 deletion(-) create mode 100644 compiler/cpp/src/thrift/generate/t_cl_generator.cc (limited to 'compiler') diff --git a/compiler/cpp/CMakeLists.txt b/compiler/cpp/CMakeLists.txt index 717b645ba..a23004110 100644 --- a/compiler/cpp/CMakeLists.txt +++ b/compiler/cpp/CMakeLists.txt @@ -73,6 +73,7 @@ endmacro() # The following compiler can be enabled or disabled THRIFT_ADD_COMPILER(c_glib "Enable compiler for C with Glib" ON) +THRIFT_ADD_COMPILER(cl "Enable compiler for Common LISP" ON) THRIFT_ADD_COMPILER(cpp "Enable compiler for C++" ON) THRIFT_ADD_COMPILER(d "Enable compiler for D" ON) THRIFT_ADD_COMPILER(dart "Enable compiler for Dart" ON) diff --git a/compiler/cpp/Makefile.am b/compiler/cpp/Makefile.am index cc776ef5e..55c82943f 100644 --- a/compiler/cpp/Makefile.am +++ b/compiler/cpp/Makefile.am @@ -70,6 +70,7 @@ thrift_SOURCES = src/thrift/audit/t_audit.cpp \ # Specific client generator source thrift_SOURCES += src/thrift/generate/t_c_glib_generator.cc \ + src/thrift/generate/t_cl_generator.cc \ src/thrift/generate/t_cpp_generator.cc \ src/thrift/generate/t_d_generator.cc \ src/thrift/generate/t_dart_generator.cc \ diff --git a/compiler/cpp/compiler.vcxproj b/compiler/cpp/compiler.vcxproj index ae77f3d0c..9fbf903a4 100644 --- a/compiler/cpp/compiler.vcxproj +++ b/compiler/cpp/compiler.vcxproj @@ -54,6 +54,7 @@ + diff --git a/compiler/cpp/src/thrift/generate/t_cl_generator.cc b/compiler/cpp/src/thrift/generate/t_cl_generator.cc new file mode 100644 index 000000000..06b6b6563 --- /dev/null +++ b/compiler/cpp/src/thrift/generate/t_cl_generator.cc @@ -0,0 +1,558 @@ +/* + * 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; + + 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; +} + +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") diff --git a/compiler/cpp/src/thrift/generate/t_generator.cc b/compiler/cpp/src/thrift/generate/t_generator.cc index a2bc9d6c2..f26690b9a 100644 --- a/compiler/cpp/src/thrift/generate/t_generator.cc +++ b/compiler/cpp/src/thrift/generate/t_generator.cc @@ -237,7 +237,7 @@ t_generator* t_generator_registry::get_generator(t_program* program, if ((language == "csharp") || (language == "netcore")) { failure("The '%s' target is no longer available. Use 'netstd' instead.", language.c_str()); } - + if (iter == the_map.end()) { return nullptr; } diff --git a/compiler/cpp/tests/CMakeLists.txt b/compiler/cpp/tests/CMakeLists.txt index fd8bed2eb..0e8254158 100644 --- a/compiler/cpp/tests/CMakeLists.txt +++ b/compiler/cpp/tests/CMakeLists.txt @@ -98,6 +98,7 @@ endmacro() # The following compiler with unit tests can be enabled or disabled THRIFT_ADD_COMPILER(c_glib "Enable compiler for C with Glib" OFF) +THRIFT_ADD_COMPILER(cl "Enable compiler for Common LISP" OFF) THRIFT_ADD_COMPILER(cpp "Enable compiler for C++" OFF) THRIFT_ADD_COMPILER(d "Enable compiler for D" OFF) THRIFT_ADD_COMPILER(dart "Enable compiler for Dart" OFF) -- cgit v1.2.1