First of all, I know the documentation is very messy as of this moment, things are clustered together with things that aren't even valid anymore. I should have more time now to clean things up as I have holidays, but then again, I also have plans. School takes up a lot of my time, especially since I travel 4+ hours a day to and from school.

Originally Posted by
javalover
You said it's an interpreted language, but then you wrote this part which tells me it's compiled:
I don't see how that part can tell you it's compiled, an interpreted language can have just the same optimizations as a compiled language. Usually when we say, compiled languages, we're talking about code that gets compiled into straight up machine code.
Ep scripts are never compiled into machine code, but into seperate blocks (called "code blocks"), each block representing some kind of token as to what the action is. For example,
is_block_type meaing, it is some type of statement that may lead into it's own code block (such as an if statement). There are many different ones, and the interpreter loops through these blocks many times to make modifications that can only be seen after an entire block has been "compiled" (note that, I use the word compiled, in a non-binary way, I am simply talking about removing certain flags, and data from statements).
After the aforementioned process, the interpreter well, interprets these blocks (seen in module::eval_internal, statement::eval_or_eval_and_return, and expression::evaluate_actions).

Originally Posted by
javalover
if it actually is, why should you create DLLs?
I haven't been very clear as to why this is, this will only happen when you use the protected
ep (*.oep or obfuscated expression parser code, or short oep) format. And as of right now, the "compiler" (It never compiles into machine code!) hasn't been released yet, or atleast newer version. It creates these temporaries because the
oep formats ability to store .dll's and ep script code. So when naturally, in ep you can import dll's using the import keyword, doing something like
Code:
import "mydll.dll";
With this information, the compiler will:
First, check syntax.
Secondly, resolve the imports. (Which will remove the import code block entirely, and instead just store a temporary string for later.)
Thirdly, generate the code. (Which will look in the relative path for all of the modules found, and just mash them into the oep file with the correct oep format.)
Lastly, save the code.
A real world example:

So running the "compiler" on the "ep.ep" script (which imports first, second, third, and fourth dll's) will generate an output file of approximately the size of all these files.
And now we get to the part where the temporary files are created.
Code:
ep::statement_list ep::module::deobfuscate_contents( std::string & contents )
{
if ( obfuscation_algorithm( contents ) )
{
auto header = *reinterpret_cast< const _Header* >( contents.data( ) );
auto copy_contents = contents;
copy_contents.erase( 0, header.size );
contents.erase( header.size, contents.size( ) );
contents.erase( 0, sizeof( _Header ) );
auto copy = copy_contents;
std::vector<std::string> libs;
while ( obfuscation_algorithm( copy ) )
{
header = *reinterpret_cast< const _Header* >( copy.data( ) );
copy_contents = copy.substr( 0, header.size );
copy_contents.erase( 0, sizeof( _Header ) );
copy.erase( 0, header.size );
libs.emplace_back( std::move( copy_contents ) );
}
copy.erase( 0, sizeof( _Header ) );
libs.emplace_back( std::move( copy ) );
statement_list statements;
ep::compiled_plugin_loader loader( std::move( libs ) );
for ( auto &x : loader.get_plugins( ) )
{
auto load = x->get_load_fn( );
auto name = x->get_name_fn( );
auto preq = x->get_prerequisites_fn( );
if ( !load || !name )
throw std::exception( "unable to load *.oep file because; one of it's modules are invalid." );
std::string sname( name( ) );
if ( is_included( sname ) )
continue;
includes_.emplace_back( sname );
if ( preq )
{
cpputils::cpp_tokenizer tokenizer;
tokenizer.tokenize( preq( ) );
if ( tokenizer.begin( )->get_block( ) == "import" )
parse_import( tokenizer.begin( ) + 1, tokenizer.end( ), statements );
}
load( engine_ );
libs_.emplace_back( x->get_path( ) );
}
return statements;
}
else
contents.erase( 0, sizeof( _Header ) );
return{ };
}
I'm not going to explain this code much more than the comments.
Now you see, libraries in
ep are nothing more but a simple plugin, here's an example of the threading library:
Code:
extern "C" __declspec( dllexport ) void load_plugin( ep::dispatch_engine_ptr engine )
{
ep::add_type( engine,
"thread",
[engine]( )
{
return std::make_shared<ep::value_reference>( new thread_proxy( ),
engine->get_type_info( "thread" ) );
},
[]( ep::value_reference *ptr )
{
delete ptr->get_pointer<void>( );
},
[]( ep::value_reference *ptr )
{
return std::make_shared<ep::value_reference>( new thread_proxy( *ptr->get_pointer<thread_proxy>( ) ),
ptr->type_info( ) );
},
"start",
[engine]( thread_proxy *obj, const std::string &func )
{
return std::make_shared<ep::value_reference>( obj->start( func, engine ),
engine->get_type_info( "thread" ),
false );
},
"is_running",
[engine]( thread_proxy *obj )
{
return std::make_shared<ep::value_reference>( new int( obj->is_running( ) ),
engine->get_type_info( "integral" ) );
},
"wait",
[engine]( thread_proxy *obj, std::chrono::milliseconds &milli )
{
obj->wait( milli );
return ep::void_return;
},
"suspend",
[]( thread_proxy *obj )
{
obj->suspend( );
return ep::void_return;
},
"resume",
[]( thread_proxy *obj )
{
obj->resume( );
return ep::void_return;
},
"kill",
[]( thread_proxy *obj )
{
obj->kill( );
return ep::void_return;
},
"sleep",
[]( thread_proxy *obj, std::chrono::milliseconds &milli )
{
obj->sleep( milli );
return ep::void_return;
} );
ep::add_type( engine,
"milliseconds",
[engine]( const int &x )
{
return std::make_shared<ep::value_reference>( new std::chrono::milliseconds( x ),
engine->get_type_info( "milliseconds" ) );
},
[]( ep::value_reference *ptr )
{
delete ptr->get_pointer<void>( );
},
[engine]( ep::value_reference *ptr )
{
return std::make_shared<ep::value_reference>( new std::chrono::milliseconds( *ptr->get_pointer<std::chrono::milliseconds>( ) ),
ptr->type_info( ) );
},
"count",
[engine]( std::chrono::milliseconds *ptr )
{
return std::make_shared<ep::value_reference>( new int( ptr->count( ) ),
engine->get_type_info( "integral" ) );
} );
engine->add_method( ep::make_method( "current_thread",
[engine]( )
{
return std::make_shared<ep::value_reference>( new thread_proxy( _Current_thread( ) ),
engine->get_type_info( "thread" ) );
} ) );
}
These plugins are required to use C++ extensions in ep, because technically,
ep has no idea about C++, internally that is.
However, C++ has an idea about
ep. And as such can call
ep methods directly in C++, this isn't compiling it, it's just a kind of smart way to do it.
How
ep function objects are created:
Code:
lambda_ = [this]( variable_list arguments, dispatch_engine_ptr engine )
{
auto state = engine->create_new_dispatch_state( get_name( ), get_arguments( ), std::move( arguments ), engine->get_global_state( ) );
for ( auto &x : get_statements( ) )
if ( x->eval_or_eval_and_return( engine, state, engine->get_debugger( ) ) )
return std::move( x->get_return( ) );
return ep::void_return;
};
Not very hard!
And to clarify, C++ and
ep communicates a lot at compile time, I utilize callable traits to get the type of each argument in C++ callables, to be able to generate the specified
ep wrapper code.
head aches
namespace ep
{
namespace details
{
namespace detail
{
template<unsigned... digits>
struct to_chars
{
static const char value[];
};
template<unsigned... digits>
const char to_chars<digits...>::value[] = { 'a', 'r', 'g', ( '0' + digits )..., 0 };
template<unsigned rem, unsigned... digits>
struct explode : explode<rem / 10, rem % 10, digits...>
{ };
template<unsigned... digits>
struct explode<0, digits...> : to_chars<digits...>
{ };
}
template<unsigned num>
struct arg_pos_to_string : detail::explode<num>
{ };
struct _Ref_ptr
{ };
template<typename T>
struct callable_traits;
template<typename Ret, typename...Params>
struct callable_traits<Ret( Params... )>
{
static constexpr auto size = sizeof...( Params );
using result_type = Ret;
typedef Ret( *signature_type )( Params... );
template<size_t pos>
struct arg_t
{
using type = typename std::tuple_element<pos, std::tuple<Params...>>::type;
};
};
template<typename _Deduce, bool ref = std::is_base_of<_Ref_ptr, _Deduce>::value>
struct _Var_deducer
{
static _Deduce deduce( ep::variable_ptr &ptr )
{
return *ptr->get_ref( )->clone( )->get_pointer<_Deduce>( );
}
};
template<typename _Deduce>
struct _Var_deducer<_Deduce*, false>
{
static _Deduce *deduce( ep::variable_ptr &ptr )
{
return ptr->get_ref( )->get_pointer<_Deduce>( );
}
};
template<typename _Deduce>
struct _Var_deducer<_Deduce&, false>
{
static _Deduce &deduce( ep::variable_ptr &ptr )
{
return *ptr->get_ref( )->get_pointer<_Deduce>( );
}
};
template<typename _Deduce>
struct _Var_deducer<_Deduce, true>
{
static _Deduce deduce( ep::variable_ptr &ptr )
{
return _Deduce( ptr );
}
};
template<>
struct _Var_deducer<variable_ptr, false>
{
static variable_ptr deduce( ep::variable_ptr &ptr )
{
return ptr;
}
};
template<size_t pos, size_t max, typename Traits, typename Ret = typename Traits::result_type>
struct _Caller
{
template<typename Func, typename...Deduced>
static auto
_Process_and_call( ep::variable_list &a,
Func &&f,
Deduced&&...deduced )
{
using arg_type = typename Traits::template arg_t<pos>::type;
return _Caller<pos + 1, max, Traits>::_Process_and_call( a,
std::forward<Func>( f ),
std::forward<Deduced>( deduced )...,
_Var_deducer<arg_type>::deduce( a[pos] ) );
}
};
template<size_t max, typename Traits, typename Ret>
struct _Caller<max, max, Traits, Ret>
{
template<typename Func, typename...Deduced>
static typename Traits::result_type
_Process_and_call( ep::variable_list &a,
Func &&f,
Deduced&&...deduced )
{
return std::forward<Func>( f )( std::forward<Deduced>( deduced )... );
}
};
template<size_t max, typename Traits>
struct _Caller<max, max, Traits, void>
{
template<typename Func, typename...Deduced>
static ep::value_reference_ptr
_Process_and_call( ep::variable_list &a,
Func &&f,
Deduced&&...deduced )
{
std::forward<Func>( f )( std::forward<Deduced>( deduced )... );
return ep::void_return;
}
};
template<size_t pos, size_t max, typename traits>
struct _Arg_emplacer
{
static void emplace( ep::method_argument_list &list )
{
using arg_t = typename traits::template arg_t<pos>::type;
list.emplace_back( new ep::method_argument( arg_pos_to_string<pos>::value,
std::is_reference<arg_t>::value ||
std::is_pointer<arg_t>::value ||
std::is_base_of<_Ref_ptr, arg_t>::value ) );
_Arg_emplacer<pos + 1, max, traits>::emplace( list );
}
};
template<size_t max, typename traits>
struct _Arg_emplacer<max, max, traits>
{
static void emplace( ep::method_argument_list &list )
{ }
};
template<typename T>
struct _Build_method_from_t
{
template<typename _Invalid>
static ep::method_ptr _Build( std::string, _Invalid &&invalid )
{
throw;
}
};
template<typename Ret, typename T, typename...Params>
struct _Build_method_from_t<Ret( T::* )( Params... ) const>
{
template<typename _Func>
static ep::method_ptr _Build( std::string name, _Func &&functor )
{
using traits = callable_traits<Ret( Params... )>;
ep::method_argument_list args;
auto method = [func = std::function<Ret( Params... )>( std::forward<_Func>( functor ) )]( ep::variable_list fargs,
ep::dispatch_engine_ptr )
{
return _Caller<0, traits::size, traits>::_Process_and_call( fargs,
func );
};
_Arg_emplacer<0, traits::size, traits>::emplace( args );
return std::make_shared<ep::method>( std::move( name ),
std::move( args ),
std::move( method ) );
}
};
template<typename Ret, typename T>
struct _Build_method_from_t<Ret( T::* )( ) const>
{
template<typename _Func>
static ep::method_ptr _Build( std::string name, _Func &&functor )
{
using traits = callable_traits<Ret( )>;
ep::method_argument_list args;
auto method = [func = std::function<Ret( )>( std::forward<_Func>( functor ) )]( ep::variable_list fargs,
ep::dispatch_engine_ptr )
{
return func( );
};
_Arg_emplacer<0, traits::size, traits>::emplace( args );
return std::make_shared<ep::method>( std::move( name ),
std::move( args ),
std::move( method ) );
}
};
template<typename T>
struct _Build_member_method_from_t
{
template<typename _Invalid>
static ep::method_ptr _Build( std::string name,
_Invalid && )
{
static_assert( false, "_Build_member_method_from_t expects callable object as T" );
}
};
template<typename Ret, typename T, typename...Params>
struct _Build_member_method_from_t<Ret( T::* )( Params... ) const>
{
template<typename _Func>
static ep::method_ptr _Build( std::string name,
_Func &&functor )
{
using traits = callable_traits<Ret( Params... )>;
ep::method_argument_list args;
auto method = [func = std::function<Ret( Params... )>( std::forward<_Func>( functor ) )]( ep::variable_list fargs,
ep::dispatch_engine_ptr )
{
return _Caller<0, traits::size, traits>::_Process_and_call( fargs,
func );
};
_Arg_emplacer<1, traits::size, traits>::emplace( args );
return std::make_shared<ep::method>( std::move( name ),
std::move( args ),
std::move( method ) );
}
};
template<typename Ret, typename T>
struct _Build_member_method_from_t<Ret( T::* )( ) const>
{
template<typename _Func>
static ep::method_ptr _Build( std::string name,
_Func &&functor )
{
using traits = callable_traits<Ret( )>;
ep::method_argument_list args;
auto method = [func = std::function<Ret( )>( std::forward<_Func>( functor ) )]( ep::variable_list fargs,
ep::dispatch_engine_ptr )
{
return _Caller<0, traits::size, traits>::_Process_and_call( fargs,
func );
};
_Arg_emplacer<1, traits::size, traits>::emplace( args );
return std::make_shared<ep::method>( std::move( name ),
std::move( args ),
std::move( method ) );
}
};
template<typename..._Specialization>
struct create_type_impl;
template<>
struct create_type_impl<>
{
static ep::type_info_ptr _Do( ep::type_info_ptr ti )
{
return std::move( ti );
}
};
template<typename Str, typename T, typename..._Specialization>
struct create_type_impl<Str, T, _Specialization...>
{
static ep::type_info_ptr _Do( ep::type_info_ptr ti,
Str &&str,
T &&t,
_Specialization&&...spec )
{
ti->add_method( std::move( _Build_member_method_from_t<decltype( &T::operator() )>::_Build( std::forward<Str>( str ),
std::forward<T>( t ) ) ) );
return create_type_impl<_Specialization...>::_Do( std::move( ti ),
std::forward<_Specialization>( spec )... );
}
};
template<typename Str, typename T>
struct create_type_impl<Str, T>
{
static ep::type_info_ptr _Do( ep::type_info_ptr ti,
Str &&str,
T &&t )
{
ti->add_method( std::move( _Build_member_method_from_t<decltype( &T::operator() )>::_Build( std::forward<Str>( str ),
std::forward<T>( t ) ) ) );
return std::move( ti );
}
};
template<size_t arity, size_t mod = arity % 2>
struct _Check_arity_impl;
template<typename..._Arity>
struct _Check_arity
{
static constexpr bool value = _Check_arity_impl<sizeof...( _Arity )>::value;
};
template<>
struct _Check_arity<>
{
static constexpr bool value = true;
};
template<size_t arity>
struct _Check_arity_impl<arity, 0>
{
static constexpr bool value = false;
};
template<size_t arity, size_t mod>
struct _Check_arity_impl
{
static constexpr bool value = true;
};
}
template<typename _Dtor, typename _Ctor, typename..._Specialization>
static ep::type_info_ptr create_type( std::string name, _Dtor &&dtor, _Ctor &&ctor, _Specialization&&...spec )
{
static_assert( !details::_Check_arity<_Specialization...>::value,
"uneven arity, expects arity of pattern (StrConvertable, Callable, Continuation...)" );
return details::create_type_impl<_Specialization...>::_Do( std::make_shared<ep::type_info>( std::move( name ),
std::forward<_Dtor>( dtor ),
std::forward<_Ctor>( ctor ) ),
std::forward<_Specialization>( spec )... );
}
template<typename _Ctor, typename _Dtor, typename _Clone, typename..._Specialization>
static void add_type( ep::module_ptr &module,
std::string name,
_Ctor &&ctor,
_Dtor &&dtor,
_Clone &&clone,
_Specialization&&...spec )
{
auto engine = module->get_engine( );
auto type = create_type( name,
std::forward<_Dtor>( dtor ),
std::forward<_Clone>( clone ),
std::forward<_Specialization>( spec )... );
engine->add_method( details::_Build_method_from_t<decltype( &_Ctor::operator() )>::_Build( std::move( name ),
std::forward<_Ctor>( ctor ) ) );
engine->add_type( std::move( type ) );
}
template<typename _Ctor, typename _Dtor, typename _Clone, typename..._Specialization>
static void add_type( ep::dispatch_engine_ptr &engine,
std::string name,
_Ctor &&ctor,
_Dtor &&dtor,
_Clone &&clone,
_Specialization&&...spec )
{
auto type = create_type( name,
std::forward<_Dtor>( dtor ),
std::forward<_Clone>( clone ),
std::forward<_Specialization>( spec )... );
engine->add_method( details::_Build_method_from_t<decltype( &_Ctor::operator() )>::_Build( std::move( name ),
std::forward<_Ctor>( ctor ) ) );
engine->add_type( std::move( type ) );
}
template<typename Str, typename T>
static auto make_method( Str && str,
T &&t )
{
return details::_Build_method_from_t<decltype( &T::operator() )>::_Build( std::forward<Str>( str ),
std::forward<T>( t ) );
}
template<typename T, typename...Ts>
static plugin_list load_plugins_impl( plugin_list &output, T &&t, Ts&&...ts )
{
output.emplace_back( load_plugin( std::forward<T>( t ) ) );
return load_plugins_impl( output, std::forward<Ts>( ts )... );
}
template<typename T>
static plugin_list load_plugins_impl( plugin_list &output, T &&t )
{
output.emplace_back( load_plugin( std::forward<T>( t ) ) );
return output;
}
template<typename...Ts>
static plugin_list load_plugins( Ts&&...ts )
{
plugin_list list;
return load_plugins_impl( list, std::forward<Ts>( ts )... );
}
// wrapper for passing variable_ptr as reference
struct argument_ptr_ref
: public details::_Ref_ptr
{
using ref = void;
variable_ptr variable;
argument_ptr_ref( ep::variable_ptr &ptr )
: variable( ptr )
{ }
};
}
EP to C++
Code:
namespace details
{
template<typename..._Params>
struct _Variable_builder;
template<typename _Next, typename ..._Params>
struct _Variable_builder<_Next, _Params...>
{
static void _Build( variable_list &list,
method_argument_list::iterator &arg,
_Next &&next,
_Params&&...rest )
{
list.emplace_back( new variable( (*arg)->get_name( ), std::make_shared<value_reference>( std::forward<_Next>( next ) ) ) );
_Variable_builder<_Params...>::_Build( list, ++arg, std::forward<_Params>( rest )... );
}
};
template<typename ..._Params>
struct _Variable_builder<variable_ptr, _Params...>
{
static void _Build( variable_list &list,
method_argument_list::iterator &arg,
variable_ptr next,
_Params&&...rest )
{
list.emplace_back( new variable( ( *arg )->get_name( ), next->get_ref( )->clone( ) ) );
_Variable_builder<_Params...>::_Build( list, ++arg, std::forward<_Params>( rest )... );
}
};
template<typename ..._Params>
struct _Variable_builder<variable_ptr&, _Params...>
{
static void _Build( variable_list &list,
method_argument_list::iterator &arg,
variable_ptr &next,
_Params&&...rest )
{
list.emplace_back( new variable( ( *arg )->get_name( ), next->steal( ) ) );
_Variable_builder<_Params...>::_Build( list, ++arg, std::forward<_Params>( rest )... );
}
};
template<typename _Last>
struct _Variable_builder<_Last>
{
static void _Build( variable_list &list,
method_argument_list::iterator &arg,
_Last &&last )
{
list.emplace_back( new variable( ( *arg )->get_name( ), std::make_shared<value_reference>( std::forward<_Last>( last ) ) ) );
}
};
template<>
struct _Variable_builder<variable_ptr&>
{
static void _Build( variable_list &list,
method_argument_list::iterator &arg,
variable_ptr &last )
{
list.emplace_back( new variable( ( *arg )->get_name( ), last->get_ref( ) ) );
}
};
template<>
struct _Variable_builder<variable_ptr>
{
static void _Build( variable_list &list,
method_argument_list::iterator &arg,
variable_ptr last )
{
list.emplace_back( new variable( ( *arg )->get_name( ), last->get_ref( )->clone( ) ) );
}
};
template<>
struct _Variable_builder<>
{
static void _Build( variable_list &list,
method_argument_list::iterator &arg )
{ }
};
template<typename T>
struct _Function_builder;
template<typename _Ret, typename ..._Params>
struct _Function_builder<_Ret( _Params... )>
{
using fn_type = std::function<_Ret( _Params... )>;
static fn_type
_Build( method_ptr method, dispatch_engine_ptr engine )
{
return [method, engine]( _Params&&...args )->_Ret
{
variable_list arguments;
_Variable_builder<_Params...>::_Build( arguments,
method->get_arguments( ).begin( ),
std::forward<_Params>( args )... );
auto result = method->get_fun( )( std::move( arguments ), engine );
return result->assume<_Ret>( );
};
}
};
template<typename..._Params>
struct _Function_builder<void( _Params... )>
{
using fn_type = std::function<void( _Params... )>;
static fn_type
_Build( method_ptr method, dispatch_engine_ptr engine )
{
return [method, engine]( _Params&&...args )->void
{
variable_list arguments;
_Variable_builder<_Params...>::_Build( arguments,
method->get_arguments( ).begin( ),
std::forward<_Params>( args )... );
method->get_fun( )( std::move( arguments ), engine );
};
}
};
template<typename..._Params>
struct _Function_builder<value_reference( _Params... )>
{
using fn_type = std::function<value_reference( _Params... )>;
static fn_type
_Build( method_ptr method, dispatch_engine_ptr engine )
{
return [method, engine]( _Params&&...args )
{
variable_list arguments;
_Variable_builder<_Params...>::_Build( arguments,
method->get_arguments( ).begin( ),
std::forward<_Params>( args )... );
return method->get_fun( )( std::move( arguments ), engine );
};
}
};
template<typename _Sig>
struct _Method_caller;
template<typename _Ret, typename ..._Params>
struct _Method_caller<_Ret( _Params... )>
{
static _Ret _Do_call( method_ptr method, dispatch_engine_ptr engine, _Params&&...params )
{
variable_list arguments;
_Variable_builder<_Params...>::_Build( arguments,
method->get_arguments( ).begin( ),
std::forward<_Params>( params )... );
auto result = method->get_fun( )( arguments, engine );
return result->assume<_Ret>( );
}
};
template<typename ..._Params>
struct _Method_caller<void( _Params... )>
{
static void _Do_call( method_ptr method, dispatch_engine_ptr engine, _Params&&...params )
{
variable_list arguments;
_Variable_builder<_Params...>::_Build( arguments,
method->get_arguments( ).begin( ),
std::forward<_Params>( params )... );
method->get_fun( )( arguments, engine );
}
};
template<typename ..._Params>
struct _Method_caller<value_reference( _Params... )>
{
static value_reference _Do_call( method_ptr method, dispatch_engine_ptr engine, _Params&&...params )
{
variable_list arguments;
_Variable_builder<_Params...>::_Build( arguments,
method->get_arguments( ).begin( ),
std::forward<_Params>( params )... );
return method->get_fun( )( arguments, engine );
}
};
}
Sorry if my explanations aren't that good, I've never been the explainy type. Feel free to add me on Discord (Yemiez#8439) for further questions!