diff options
author | John Hodge <tpg@mutabah.net> | 2015-04-03 22:57:27 +0800 |
---|---|---|
committer | John Hodge <tpg@mutabah.net> | 2015-04-03 22:57:27 +0800 |
commit | c15006c15505ae785eb5447c055b8f9379e7fcde (patch) | |
tree | afe7f047db2c5ab2e0ea7bbf7155522771be474a /src/synexts/derive.cpp | |
parent | 44e15eac335bfe8a78af259541be97d1f6653d7a (diff) | |
download | mrust-c15006c15505ae785eb5447c055b8f9379e7fcde.tar.gz |
Added partial support for #[derive()]
- Supports Debug on struct, and assumes compiling in libcore (for now)
Diffstat (limited to 'src/synexts/derive.cpp')
-rw-r--r-- | src/synexts/derive.cpp | 151 |
1 files changed, 151 insertions, 0 deletions
diff --git a/src/synexts/derive.cpp b/src/synexts/derive.cpp new file mode 100644 index 00000000..ccb4fb00 --- /dev/null +++ b/src/synexts/derive.cpp @@ -0,0 +1,151 @@ + +#include <synext.hpp> +#include "../common.hpp" +#include "../ast/ast.hpp" +#include "../ast/expr.hpp" + +template<typename T> +static inline ::std::vector<T> vec$(T v1) { + ::std::vector<T> tmp; + tmp.push_back( mv$(v1) ); + return mv$(tmp); +} +template<typename T> +static inline ::std::vector<T> vec$(T v1, T v2) { + ::std::vector<T> 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(const AST::TypeParams& 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(const AST::TypeParams& p, const TypeRef& type, const AST::Struct& str) const override + { + const AST::Path debug_trait("", { AST::PathNode("fmt", {}), AST::PathNode("Debug", {}) }); + const TypeRef ret_type(AST::Path("", {AST::PathNode("fmt",{}), AST::PathNode("Result",{})}) ); + const TypeRef f_type(TypeRef::TagReference(), true, + TypeRef(AST::Path("", {AST::PathNode("fmt",{}), AST::PathNode("Formatter", {})})) + ); + const ::std::string& name = type.path().nodes().back().name(); + + // Generate code for Debug + AST::ExprNodeP node; + 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 : str.fields() ) + { + node = NEWNODE(AST::ExprNode_CallMethod, + mv$(node), AST::PathNode("field",{}), + vec$( + NEWNODE(AST::ExprNode_String, fld.name), + NEWNODE(AST::ExprNode_UniOp, AST::ExprNode_UniOp::REF, + NEWNODE(AST::ExprNode_Field, + NEWNODE(AST::ExprNode_NamedValue, AST::Path("self")), + fld.name + ) + ) + ) + ); + } + node = NEWNODE(AST::ExprNode_CallMethod, mv$(node), AST::PathNode("finish",{}), {}); + + DEBUG("node = " << *node); + + AST::Function fcn( + AST::MetaItems(), AST::TypeParams(), AST::Function::CLASS_REFMETHOD, + ret_type, + vec$( ::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::Module>()) ); + + AST::Impl rv( AST::MetaItems(), p, type, debug_trait ); + 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<typename T> +static void derive_item(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"); + + DEBUG("path = " << path); + bool fail = false; + + const auto& params = item.params(); + TypeRef type(path); + for( const auto& param : params.ty_params() ) + type.path().nodes().back().args().push_back( TypeRef(TypeRef::TagArg(), param.name()) ); + + for( const auto& trait : attr.items().m_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(params, type, item) ); + } + + if( fail ) { + //throw CompileError::Generic("Failed to #[dervie]"); + } +} + +class Decorator_Derive: + public CDecoratorHandler +{ +public: + void handle_item(AST::Module& mod, const AST::MetaItem& attr, const AST::Path& path, AST::Struct& str) const override + { + derive_item(mod, attr, path, str); + } +}; + +STATIC_SYNEXT(Decorator, "derive", Decorator_Derive) + |