diff options
-rw-r--r-- | Makefile | 3 | ||||
-rw-r--r-- | src/ast/ast.hpp | 5 | ||||
-rw-r--r-- | src/ast/expr.hpp | 5 | ||||
-rw-r--r-- | src/ast/path.hpp | 5 | ||||
-rw-r--r-- | src/convert/decorators.cpp | 69 | ||||
-rw-r--r-- | src/include/main_bindings.hpp | 11 | ||||
-rw-r--r-- | src/include/synext.hpp | 33 | ||||
-rw-r--r-- | src/main.cpp | 1 | ||||
-rw-r--r-- | src/parse/expr.cpp | 4 | ||||
-rw-r--r-- | src/parse/lex.hpp | 2 | ||||
-rw-r--r-- | src/synexts/derive.cpp | 151 |
11 files changed, 283 insertions, 6 deletions
@@ -21,8 +21,11 @@ OBJ += parse/parseerror.o parse/lex.o OBJ += parse/root.o parse/paths.o parse/types.o parse/expr.o parse/pattern.o OBJ += dump_as_rust.o OBJ += convert/ast_iterate.o +OBJ += convert/decorators.o OBJ += convert/resolve.o convert/typecheck_bounds.o convert/typecheck_params.o convert/typecheck_expr.o OBJ += convert/flatten.o convert/render.o +OBJ += synexts/derive.o + OBJ := $(addprefix $(OBJDIR),$(OBJ)) 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<StructItem>& fields() const { return m_fields; }
+ MetaItems& attrs() { return m_attrs; }
TypeParams& params() { return m_params; }
::std::vector<StructItem>& 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<unique_ptr<ExprNode>> m_args; ExprNode_CallMethod() {} - ExprNode_CallMethod(unique_ptr<ExprNode>&& obj, PathNode&& method, ::std::vector<unique_ptr<ExprNode>>&& args): + ExprNode_CallMethod(unique_ptr<ExprNode> obj, PathNode method, ::std::vector<unique_ptr<ExprNode>> 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<ExprNode>&& obj, ::std::string&& name): + ExprNode_Field(::std::unique_ptr<ExprNode>&& 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<AST::ExprNode> 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 <main_bindings.hpp> +#include <unordered_map> // C++11, hashmap +#include <synext.hpp> + +::std::unordered_map< ::std::string, ::std::unique_ptr<CDecoratorHandler> > g_decorators; + +template<typename T> +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<AST::Module*> 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<CDecoratorHandler> handler) +{ + auto res = g_decorators.insert( ::std::make_pair(name, mv$(handler)) ); + if( res.second == false ) + { + DEBUG("Duplicate definition of decorator '"<<name<<"'"); + } +} + +void Process_Decorators(AST::Crate& crate) +{ + CProcessor processor(crate); + + processor.handle_module(AST::Path({}), crate.root_module()); +} + diff --git a/src/include/main_bindings.hpp b/src/include/main_bindings.hpp index fef57184..91bac742 100644 --- a/src/include/main_bindings.hpp +++ b/src/include/main_bindings.hpp @@ -5,13 +5,24 @@ #include "../ast/ast.hpp" +/// Parse a crate from the given file extern AST::Crate Parse_Crate(::std::string mainfile); +/// Process #[] decorators +extern void Process_Decorators(AST::Crate& crate); +/// Resolve all in-text paths to absolute variants extern void ResolvePaths(AST::Crate& crate); +/// Check that generic bounds are valid extern void Typecheck_GenericBounds(AST::Crate& crate); +/// Check that parameters for generics are valid extern void Typecheck_GenericParams(AST::Crate& crate); +/// Type resolution (and hence checking) for expressions extern void Typecheck_Expr(AST::Crate& crate); + +/// Convert the AST to a flat tree extern AST::Flat Convert_Flatten(const AST::Crate& crate); + +/// Dump the crate as annotated rust extern void Dump_Rust(const char *Filename, const AST::Crate& crate); #endif diff --git a/src/include/synext.hpp b/src/include/synext.hpp new file mode 100644 index 00000000..6688c1c7 --- /dev/null +++ b/src/include/synext.hpp @@ -0,0 +1,33 @@ +/* + */ +#ifndef _SYNEXT_HPP_ +#define _SYNEXT_HPP_ + +#include "../common.hpp" // for mv$ and other things +#include <string> +#include <memory> + +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<C##_type##Handler>(new _typename()) ); \ + } \ + } s_register_##_typename; + +extern void Register_Synext_Decorator(::std::string name, ::std::unique_ptr<CDecoratorHandler> 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 <iostream>
#include "tokentree.hpp"
-typedef ::std::unique_ptr<AST::ExprNode> 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 <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) + |