#include #include "../common.hpp" #include "../ast/ast.hpp" #include "../ast/expr.hpp" template static inline ::std::vector vec$(T v1) { ::std::vector tmp; tmp.push_back( mv$(v1) ); return mv$(tmp); } template static inline ::std::vector vec$(T v1, T v2) { ::std::vector tmp; tmp.push_back( mv$(v1) ); tmp.push_back( mv$(v2) ); return mv$(tmp); } static inline AST::ExprNodeP mk_exprnodep(AST::ExprNode* en){ return AST::ExprNodeP(en); } #define NEWNODE(type, ...) mk_exprnodep(new type(__VA_ARGS__)) /// Interface for derive handlers struct Deriver { virtual AST::Impl handle_item(Span sp, const AST::GenericParams& params, const TypeRef& type, const AST::Struct& str) const = 0; }; /// 'Debug' derive handler class Deriver_Debug: public Deriver { //static AST::ExprNodeP _print_l(::std::string val) //{ // return NEWNODE(AST::ExprNode_CallMethod, // NEWNODE(AST::ExprNode_NamedValue, AST::Path("f")), // AST::PathNode("write_str",{}), // { NEWNODE(AST::ExprNode_String, mv$(val)) } // ); //} //static AST::ExprNodeP _try(AST::ExprNodeP expr) //{ // throw CompileError::Todo("derive(Debug) - _try"); //} public: AST::Impl handle_item(Span sp, const AST::GenericParams& p, const TypeRef& type, const AST::Struct& str) const override { // TODO: be correct herhe and use "core" as the crate name // - Requires handling the crate_name crate attribute correctly const AST::Path debug_trait("", { AST::PathNode("fmt", {}), AST::PathNode("Debug", {}) }); const TypeRef ret_type(sp, AST::Path("", {AST::PathNode("fmt",{}), AST::PathNode("Result",{})}) ); const TypeRef f_type(TypeRef::TagReference(), sp, true, TypeRef(sp, AST::Path("", {AST::PathNode("fmt",{}), AST::PathNode("Formatter", {})})) ); const ::std::string& name = type.path().nodes().back().name(); // Generate code for Debug AST::ExprNodeP node; TU_MATCH(AST::StructData, (str.m_data), (e), (Struct, node = NEWNODE(AST::ExprNode_NamedValue, AST::Path("f")); node = NEWNODE(AST::ExprNode_CallMethod, mv$(node), AST::PathNode("debug_struct",{}), vec$( NEWNODE(AST::ExprNode_String, name) ) ); for( const auto& fld : e.ents ) { node = NEWNODE(AST::ExprNode_CallMethod, mv$(node), AST::PathNode("field",{}), vec$( NEWNODE(AST::ExprNode_String, fld.m_name), NEWNODE(AST::ExprNode_UniOp, AST::ExprNode_UniOp::REF, NEWNODE(AST::ExprNode_Field, NEWNODE(AST::ExprNode_NamedValue, AST::Path("self")), fld.m_name ) ) ) ); } node = NEWNODE(AST::ExprNode_CallMethod, mv$(node), AST::PathNode("finish",{}), {}); ), (Tuple, node = NEWNODE(AST::ExprNode_NamedValue, AST::Path("f")); node = NEWNODE(AST::ExprNode_CallMethod, mv$(node), AST::PathNode("debug_tuple",{}), vec$( NEWNODE(AST::ExprNode_String, name) ) ); for( unsigned int idx = 0; idx < e.ents.size(); idx ++ ) { node = NEWNODE(AST::ExprNode_CallMethod, mv$(node), AST::PathNode("field",{}), vec$( NEWNODE(AST::ExprNode_UniOp, AST::ExprNode_UniOp::REF, NEWNODE(AST::ExprNode_Field, NEWNODE(AST::ExprNode_NamedValue, AST::Path("self")), FMT(idx) ) ) ) ); } node = NEWNODE(AST::ExprNode_CallMethod, mv$(node), AST::PathNode("finish",{}), {}); ) ) DEBUG("node = " << *node); AST::Function fcn( AST::GenericParams(), ret_type, vec$( ::std::make_pair( AST::Pattern(AST::Pattern::TagBind(), "self"), TypeRef(TypeRef::TagReference(), sp, false, TypeRef("Self")) ), ::std::make_pair( AST::Pattern(AST::Pattern::TagBind(), "f"), f_type ) ) ); fcn.set_code( NEWNODE(AST::ExprNode_Block, vec$(mv$(node)), ::std::unique_ptr()) ); AST::Impl rv( AST::ImplDef( sp, AST::MetaItems(), p, make_spanned(sp, debug_trait), type ) ); rv.add_function(false, "fmt", mv$(fcn)); return mv$(rv); } } g_derive_debug; // -------------------------------------------------------------------- // Select and dispatch the correct derive() handler // -------------------------------------------------------------------- static const Deriver* find_impl(const ::std::string& trait_name) { if( trait_name == "Debug" ) return &g_derive_debug; else return nullptr; } template static void derive_item(const Span& sp, AST::Module& mod, const AST::MetaItem& attr, const AST::Path& path, const T& item) { if( !attr.has_sub_items() ) { //throw CompileError::Generic("#[derive()] requires a list of known traits to derive"); return ; } DEBUG("path = " << path); bool fail = false; const auto& params = item.params(); TypeRef type(sp, path); auto& types_args = type.path().nodes().back().args(); for( const auto& param : params.ty_params() ) { types_args.m_types.push_back( TypeRef(TypeRef::TagArg(), param.name()) ); } for( const auto& trait : attr.items() ) { DEBUG("- " << trait.name()); auto dp = find_impl(trait.name()); if( !dp ) { DEBUG("> No handler for " << trait.name()); fail = true; continue ; } mod.add_impl( dp->handle_item(sp, params, type, item) ); } if( fail ) { //throw CompileError::Generic("Failed to #[dervie]"); } } class Decorator_Derive: public ExpandDecorator { public: AttrStage stage() const override { return AttrStage::LatePost; } void handle(const Span& sp, const AST::MetaItem& attr, ::AST::Crate& crate, const AST::Path& path, AST::Module& mod, AST::Item& i) const override { TU_MATCH_DEF(::AST::Item, (i), (e), ( ), (Struct, derive_item(sp, mod, attr, path, e); ) ) } }; STATIC_DECORATOR("derive", Decorator_Derive)