From c15006c15505ae785eb5447c055b8f9379e7fcde Mon Sep 17 00:00:00 2001 From: John Hodge Date: Fri, 3 Apr 2015 22:57:27 +0800 Subject: Added partial support for #[derive()] - Supports Debug on struct, and assumes compiling in libcore (for now) --- src/ast/ast.hpp | 5 +- src/ast/expr.hpp | 5 +- src/ast/path.hpp | 5 +- src/convert/decorators.cpp | 69 +++++++++++++++++++ src/include/main_bindings.hpp | 11 +++ src/include/synext.hpp | 33 +++++++++ src/main.cpp | 1 + src/parse/expr.cpp | 4 +- src/parse/lex.hpp | 2 + src/synexts/derive.cpp | 151 ++++++++++++++++++++++++++++++++++++++++++ 10 files changed, 280 insertions(+), 6 deletions(-) create mode 100644 src/convert/decorators.cpp create mode 100644 src/include/synext.hpp create mode 100644 src/synexts/derive.cpp (limited to 'src') diff --git a/src/ast/ast.hpp b/src/ast/ast.hpp index 5cf1dee2..1cfae202 100644 --- a/src/ast/ast.hpp +++ b/src/ast/ast.hpp @@ -170,6 +170,7 @@ public: const ::std::string& name() const { return m_name; } const ::std::string& string() const { return m_str_val; } bool has_sub_items() const { return m_sub_items.m_items.size() > 0; } + const MetaItems& items() const { return m_sub_items; } MetaItems& items() { return m_sub_items; } friend ::std::ostream& operator<<(::std::ostream& os, const MetaItem& x) { @@ -454,9 +455,11 @@ public: m_fields( move(fields) ) {} - const TypeParams& params() const { return m_params; } + const MetaItems& attrs() const { return m_attrs; } + const TypeParams& params() const { return m_params; } const ::std::vector& fields() const { return m_fields; } + MetaItems& attrs() { return m_attrs; } TypeParams& params() { return m_params; } ::std::vector& fields() { return m_fields; } diff --git a/src/ast/expr.hpp b/src/ast/expr.hpp index 663beae0..45f6e3c9 100644 --- a/src/ast/expr.hpp +++ b/src/ast/expr.hpp @@ -208,7 +208,7 @@ struct ExprNode_CallMethod: ::std::vector> m_args; ExprNode_CallMethod() {} - ExprNode_CallMethod(unique_ptr&& obj, PathNode&& method, ::std::vector>&& args): + ExprNode_CallMethod(unique_ptr obj, PathNode method, ::std::vector> args): m_val( move(obj) ), m_method( move(method) ), m_args( move(args) ) @@ -479,7 +479,7 @@ struct ExprNode_Field: ::std::string m_name; ExprNode_Field() {} - ExprNode_Field(::std::unique_ptr&& obj, ::std::string&& name): + ExprNode_Field(::std::unique_ptr&& obj, ::std::string name): m_obj( ::std::move(obj) ), m_name( ::std::move(name) ) { @@ -723,6 +723,7 @@ public: SERIALISABLE_PROTOTYPES(); }; +typedef ::std::unique_ptr ExprNodeP; } #endif diff --git a/src/ast/path.hpp b/src/ast/path.hpp index a2ca316b..390271fe 100644 --- a/src/ast/path.hpp +++ b/src/ast/path.hpp @@ -180,7 +180,10 @@ public: struct TagLocal {}; Path(TagLocal, ::std::string name): m_class(LOCAL), - m_nodes({PathNode(name, {})}) + m_nodes({PathNode( ::std::move(name), {} )}) + {} + Path(::std::string name): + Path(TagLocal(), ::std::move(name)) {} struct TagSuper {}; Path(TagSuper): diff --git a/src/convert/decorators.cpp b/src/convert/decorators.cpp new file mode 100644 index 00000000..0ccb16d4 --- /dev/null +++ b/src/convert/decorators.cpp @@ -0,0 +1,69 @@ +/* + */ +#include "ast_iterate.hpp" +#include "../ast/ast.hpp" +#include +#include // C++11, hashmap +#include + +::std::unordered_map< ::std::string, ::std::unique_ptr > g_decorators; + +template +bool Decorator_Apply(AST::Module& mod, const AST::MetaItem& attr, const AST::Path& path, T& ent) +{ + auto it = g_decorators.find(attr.name()); + if( it == g_decorators.end() ) + { + return false; + } + + const CDecoratorHandler& handler = *it->second; + + handler.handle_item(mod, attr, path, ent); + return true; +} + +class CProcessor: + public CASTIterator +{ + AST::Crate& m_crate; + ::std::vector m_modstack; +public: + CProcessor(AST::Crate& crate): + m_crate(crate) + {} + + void handle_module(AST::Path path, AST::Module& mod) override + { + m_modstack.push_back(&mod); + CASTIterator::handle_module(mv$(path), mod); + m_modstack.pop_back(); + } + + void handle_struct(AST::Path path, AST::Struct& str) override + { + // For all attributes on the struct, search for a handler and call handler + auto& attrs = str.attrs(); + for( auto& attr : attrs.m_items ) + { + Decorator_Apply(*m_modstack.back(), attr, path, str); + } + } +}; + +void Register_Synext_Decorator(::std::string name, ::std::unique_ptr handler) +{ + auto res = g_decorators.insert( ::std::make_pair(name, mv$(handler)) ); + if( res.second == false ) + { + DEBUG("Duplicate definition of decorator '"< +#include + +namespace AST { + class MetaItem; + class Path; + class Module; + class Struct; +} + +class CDecoratorHandler +{ +public: + virtual void handle_item(AST::Module& mod, const AST::MetaItem& attr, const AST::Path& path, AST::Struct& str) const = 0; +}; + +#define STATIC_SYNEXT(_type, ident, _typename) \ + struct register_##_typename##_c {\ + register_##_typename##_c() {\ + Register_Synext_##_type( ident, ::std::unique_ptr(new _typename()) ); \ + } \ + } s_register_##_typename; + +extern void Register_Synext_Decorator(::std::string name, ::std::unique_ptr handler); + +#endif + diff --git a/src/main.cpp b/src/main.cpp index d996167b..339fdb1e 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -57,6 +57,7 @@ int main(int argc, char *argv[]) // Iterate all items in the AST, applying syntax extensions g_cur_phase = "Syn Exts"; + Process_Decorators(crate); // TODO: g_cur_phase = "PostParse"; diff --git a/src/parse/expr.cpp b/src/parse/expr.cpp index 536860c6..e83d2dbe 100644 --- a/src/parse/expr.cpp +++ b/src/parse/expr.cpp @@ -17,10 +17,10 @@ #include #include "tokentree.hpp" -typedef ::std::unique_ptr ExprNodeP; +using AST::ExprNode; +using AST::ExprNodeP; static inline ExprNodeP mk_exprnodep(const TokenStream& lex, AST::ExprNode* en){en->set_pos(lex.getPosition()); return ExprNodeP(en); } #define NEWNODE(type, ...) mk_exprnodep(lex, new type(__VA_ARGS__)) -using AST::ExprNode; ExprNodeP Parse_ExprBlockNode(TokenStream& lex); ExprNodeP Parse_Stmt(TokenStream& lex); diff --git a/src/parse/lex.hpp b/src/parse/lex.hpp index 0dab9a24..522d2d31 100644 --- a/src/parse/lex.hpp +++ b/src/parse/lex.hpp @@ -105,7 +105,9 @@ extern ::std::ostream& operator<<(::std::ostream& os, const Token& tok); /// State the parser needs to pass down via a second channel. struct ParseState { + // Used for "for/if/while" to handle ambiguity bool disallow_struct_literal = false; + // A debugging hook that disables expansion of macros bool no_expand_macros = false; friend ::std::ostream& operator<<(::std::ostream& os, const ParseState& ps) { 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 +#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(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::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 +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) + -- cgit v1.2.3