Skip to content

Commit

Permalink
fixed header only compile mode for Engine and CoroutineScriptEngine ...
Browse files Browse the repository at this point in the history
... for the case the corresponding headers were included in more
    than one .cpp file.

- set version to 0.15.1
  • Loading branch information
Florian Thake committed Nov 3, 2024
1 parent 80f62e9 commit 51977e6
Show file tree
Hide file tree
Showing 5 changed files with 83 additions and 35 deletions.
47 changes: 29 additions & 18 deletions include/teascript/CoroutineScriptEngine.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,21 @@ using ProgramPtr = std::shared_ptr<Program>;
} // namespace StackVM


// header only and non-header only compile mode handling.
#if !defined(TEASCRIPT_DISABLE_HEADER_ONLY)
# define TEASCRIPT_DISABLE_HEADER_ONLY 0
#endif
// depending on the mode we need to declare member functions inline or not for resolve linker issues.
#if !defined( TEASCRIPT_COMPILE_MODE_INLINE )
# if TEASCRIPT_DISABLE_HEADER_ONLY
# define TEASCRIPT_COMPILE_MODE_INLINE
# else
# define TEASCRIPT_COMPILE_MODE_INLINE inline
# endif
#endif



/// The class CoroutineScriptEngine can be used for execute TeaScript code similar
/// like coroutines. The scripts can be suspended (by themselves, by constraints or
/// by request) and they are able to yield values at any point and continue execution
Expand Down Expand Up @@ -55,34 +70,34 @@ class CoroutineScriptEngine

public:
/// Default constructor will bootstrap the full Core Library into the context. No coroutine loaded yet.
CoroutineScriptEngine();
TEASCRIPT_COMPILE_MODE_INLINE CoroutineScriptEngine();
/// Will use the given context as the context for the coroutine (see class teascript::ContextFactory for a handy helper.)
/// \note any prior existing local scope will be removed from the context.
explicit CoroutineScriptEngine( Context &&rContext );
TEASCRIPT_COMPILE_MODE_INLINE explicit CoroutineScriptEngine( Context &&rContext );
/// Will prepare to execute the given program as coroutine and bootstrap the full Core Library into the context.
explicit CoroutineScriptEngine( StackVM::ProgramPtr const &coroutine );
TEASCRIPT_COMPILE_MODE_INLINE explicit CoroutineScriptEngine( StackVM::ProgramPtr const &coroutine );
/// Will prepare to execute the given program as coroutine and use the given context as the context for the coroutine.
/// \note any prior existing local scope will be removed from the context.
CoroutineScriptEngine( StackVM::ProgramPtr const &coroutine, Context &&rContext );
TEASCRIPT_COMPILE_MODE_INLINE CoroutineScriptEngine( StackVM::ProgramPtr const &coroutine, Context &&rContext );

/// builds a coroutine program from given source.
/// \see also teascript::StackVM::Program::Load()
/// \see also class teascript::Engine for more possibilities to compile a program.
static StackVM::ProgramPtr Build( Content const &rContent, eOptimize const opt_level = eOptimize::O0, std::string const &name = "_USER_CORO_" );
TEASCRIPT_COMPILE_MODE_INLINE static StackVM::ProgramPtr Build( Content const &rContent, eOptimize const opt_level = eOptimize::O0, std::string const &name = "_USER_CORO_" );

/// Will prepare to execute the given program as coroutine, the old coroutine will be removed.
/// The current coroutine must not be running actually!
void ChangeCoroutine( StackVM::ProgramPtr const &coroutine );
TEASCRIPT_COMPILE_MODE_INLINE void ChangeCoroutine( StackVM::ProgramPtr const &coroutine );

/// Resets state and prepares actual set coroutine for execution. same as ChangeCoroutine( old_coroutine ).
void Reset();
TEASCRIPT_COMPILE_MODE_INLINE void Reset();

/// \returns whether the coroutine is neither running, nor yet finished and no error occurred, so that in can be continued (e.g., for yielding more values)
bool CanBeContinued() const;
TEASCRIPT_COMPILE_MODE_INLINE bool CanBeContinued() const;

/// \returns whether the coroutine is completely finished (no more values can be yielded / no instruction left to be executed).
/// \note depending on the coroutine code this state might be never reached!
bool IsFinished() const;
TEASCRIPT_COMPILE_MODE_INLINE bool IsFinished() const;

/// \returns whether the actual set coroutine is running, i.e. a thread is inside Run()/operator()/RunFor() or ChangeCoroutine().
inline
Expand All @@ -94,18 +109,18 @@ class CoroutineScriptEngine

/// \returns whether on this platform it is possible to send a suspend request to a running coroutine from another thread.
/// \note: see class teascript::StackVM::Machine for details.
bool IsSuspendRequestPossible() const;
TEASCRIPT_COMPILE_MODE_INLINE bool IsSuspendRequestPossible() const;

/// sends a suspend request to the (running) coroutine from (most likely) a different thread.
/// \returns true if it make sense to wait for coroutine is suspeneded, false if a request could not be sent (error).
/// \see class teascript::StackVM::Machine::Suspend() for details.
bool Suspend() const;
TEASCRIPT_COMPILE_MODE_INLINE bool Suspend() const;

/// Runs the coroutine until yield, suspend, finished or error occurred.
/// \returns the yielded value if any, or NaV (Not A Value).
/// \throws exception::runtime_error or a derived class, or (rarely) only a std::exception based exception.
/// \note A running coroutine can be suspended from another thread via Suspend() if IsSuspendRequestPossible() returns true.
ValueObject Run();
TEASCRIPT_COMPILE_MODE_INLINE ValueObject Run();

/// Runs the coroutine until yield, suspend, finished or error occurred.
/// \returns the yielded value if any, or NaV (Not A Value).
Expand All @@ -120,12 +135,12 @@ class CoroutineScriptEngine
/// \returns the yielded value if any, or NaV (Not A Value).
/// \throws exception::runtime_error or a derived class, or (rarely) only a std::exception based exception.
/// \note A running coroutine can be suspended from another thread via Suspend() if IsSuspendRequestPossible() returns true.
ValueObject RunFor( StackVM::Constraints const &constraint );
TEASCRIPT_COMPILE_MODE_INLINE ValueObject RunFor( StackVM::Constraints const &constraint );

/// Adds given ValueObjects as a tuple "args[idx]". Additionally adding an "argN" variable indicating the parameter amount. The coroutine must be suspended.
/// The ValueObjects for the parameters must be in shared state (created with ValueShared or a MakeShared() call issued).
/// \note this function is _not_ thread safe. Only one thread is allowed to call this function the _same_ time and the coroutine must not be running!
void SetInputParameters( std::vector<ValueObject> const &params );
TEASCRIPT_COMPILE_MODE_INLINE void SetInputParameters( std::vector<ValueObject> const &params );

/// Adds given parameters as ValueObjects as a tuple "args[idx]". Additionally adding an "argN" variable indicating the parameter amount. The coroutine must be suspended.
/// \note this function is _not_ thread safe. Only one thread is allowed to call this function the _same_ time and the coroutine must not be running!
Expand All @@ -140,10 +155,6 @@ class CoroutineScriptEngine
} // namespace teascript


#if !defined(TEASCRIPT_DISABLE_HEADER_ONLY)
# define TEASCRIPT_DISABLE_HEADER_ONLY 0
#endif

// check for broken header only / not header only configurations.
#if (0==TEASCRIPT_DISABLE_HEADER_ONLY) && defined(TEASCRIPT_INCLUDE_DEFINITIONS)
# error header only config broken, TEASCRIPT_DISABLE_HEADER_ONLY is 0 but TEASCRIPT_INCLUDE_DEFINITIONS is defined.
Expand Down
14 changes: 14 additions & 0 deletions include/teascript/CoroutineScriptEngine_Impl.ipp
Original file line number Diff line number Diff line change
Expand Up @@ -23,25 +23,29 @@

namespace teascript {

TEASCRIPT_COMPILE_MODE_INLINE
CoroutineScriptEngine::CoroutineScriptEngine()
: mMachine( std::make_shared<teascript::StackVM::Machine<true>>() )
{
CoreLibrary().Bootstrap( mContext, config::full() ); // we always use the complete core library by default.
}

TEASCRIPT_COMPILE_MODE_INLINE
CoroutineScriptEngine::CoroutineScriptEngine( Context &&rContext )
: mContext( std::move( rContext ) )
, mMachine( std::make_shared<teascript::StackVM::Machine<true>>() )
{
}

TEASCRIPT_COMPILE_MODE_INLINE
CoroutineScriptEngine::CoroutineScriptEngine( StackVM::ProgramPtr const &coroutine )
: mMachine( std::make_shared<teascript::StackVM::Machine<true>>() )
{
CoreLibrary().Bootstrap( mContext, config::full() ); // we always use the complete core library by default.
ChangeCoroutine( coroutine );
}

TEASCRIPT_COMPILE_MODE_INLINE
CoroutineScriptEngine::CoroutineScriptEngine( StackVM::ProgramPtr const &coroutine, Context &&rContext )
: mContext( std::move( rContext ) )
, mMachine( std::make_shared<teascript::StackVM::Machine<true>>() )
Expand All @@ -50,6 +54,7 @@ CoroutineScriptEngine::CoroutineScriptEngine( StackVM::ProgramPtr const &corouti
}

/*static*/
TEASCRIPT_COMPILE_MODE_INLINE
StackVM::ProgramPtr CoroutineScriptEngine::Build( Content const &rContent, eOptimize const opt_level, std::string const &name )
{
Parser p;
Expand All @@ -58,6 +63,7 @@ StackVM::ProgramPtr CoroutineScriptEngine::Build( Content const &rContent, eOpti
return c.Compile( p.Parse( rContent, name ), opt_level );
}

TEASCRIPT_COMPILE_MODE_INLINE
void CoroutineScriptEngine::ChangeCoroutine( StackVM::ProgramPtr const &coroutine )
{
bool expected = false;
Expand All @@ -78,41 +84,48 @@ void CoroutineScriptEngine::ChangeCoroutine( StackVM::ProgramPtr const &coroutin
mMachine->ThrowPossibleErrorException();
}

TEASCRIPT_COMPILE_MODE_INLINE
void CoroutineScriptEngine::Reset()
{
auto current = mMachine->GetMainProgram(); // copy is intended!
ChangeCoroutine( current );
}

TEASCRIPT_COMPILE_MODE_INLINE
bool CoroutineScriptEngine::CanBeContinued() const
{
// NOTE: the potential race between && is ok, the state may change after the call anyway.
// The target is to protect RunFor + ChangeCoroutine!
return not IsRunning() && mMachine->IsSuspended();
}

TEASCRIPT_COMPILE_MODE_INLINE
bool CoroutineScriptEngine::IsFinished() const
{
// NOTE: the potential race between && is ok, the state may change after the call anyway.
// The target is to protect RunFor + ChangeCoroutine!
return not IsRunning() && mMachine->IsFinished();
}

TEASCRIPT_COMPILE_MODE_INLINE
bool CoroutineScriptEngine::IsSuspendRequestPossible() const
{
return mMachine->SuspendRequestPossible();
}

TEASCRIPT_COMPILE_MODE_INLINE
bool CoroutineScriptEngine::Suspend() const
{
return mMachine->Suspend();
}

TEASCRIPT_COMPILE_MODE_INLINE
ValueObject CoroutineScriptEngine::Run()
{
return RunFor( StackVM::Constraints::None() );
}

TEASCRIPT_COMPILE_MODE_INLINE
ValueObject CoroutineScriptEngine::RunFor( StackVM::Constraints const &constraint )
{
bool expected = false;
Expand All @@ -130,6 +143,7 @@ ValueObject CoroutineScriptEngine::RunFor( StackVM::Constraints const &constrain
return {};
}

TEASCRIPT_COMPILE_MODE_INLINE
void CoroutineScriptEngine::SetInputParameters( std::vector<ValueObject> const &params )
{
// NOTE: This checks don't make the call threadsafe, it is just to potentially detect wrong usage.
Expand Down
43 changes: 27 additions & 16 deletions include/teascript/Engine.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,20 @@ using ProgramPtr = std::shared_ptr<Program>;
} // namespace StackVM


// header only and non-header only compile mode handling.
#if !defined(TEASCRIPT_DISABLE_HEADER_ONLY)
# define TEASCRIPT_DISABLE_HEADER_ONLY 0
#endif
// depending on the mode we need to declare member functions inline or not for resolve linker issues.
#if !defined( TEASCRIPT_COMPILE_MODE_INLINE )
# if TEASCRIPT_DISABLE_HEADER_ONLY
# define TEASCRIPT_COMPILE_MODE_INLINE
# else
# define TEASCRIPT_COMPILE_MODE_INLINE inline
# endif
#endif


/// The TeaScript standard engine.
/// This is a single-thread engine. You can use an instance of this class in
/// one thread. If you use the same(!) instance in more than one thread,
Expand Down Expand Up @@ -79,18 +93,18 @@ class Engine : public EngineBase
/// If \param bootstrap is true it will bootstrap the Core Library with specified config from \param config.
/// \note This constructor is useful for derived classes which don't want the default bootstrapping, e.g.
/// using another CoreLibrary or a derived class. Don't forget to override ResetState() in such a case.
Engine( bool const bootstrap, config::eConfig const config, eMode const mode = eMode::Compile, eOptimize const opt_level = eOptimize::O0 );
TEASCRIPT_COMPILE_MODE_INLINE Engine( bool const bootstrap, config::eConfig const config, eMode const mode = eMode::Compile, eOptimize const opt_level = eOptimize::O0 );

/// Adds the given ValuObject \param val to the current scope as name \param rName.
/// \throw May throw exception::redefinition_of_variable or a different exception based on exception::eval_eror/runtime_error.
void AddValueObject( std::string const &rName, ValueObject val ) override;
TEASCRIPT_COMPILE_MODE_INLINE void AddValueObject( std::string const &rName, ValueObject val ) override;

/// Evaluates/Executes the given content as TeaScript.
/// Depending on the mode the content will be either parsed and evaluated or parsed, compiled and executed.
/// \param rContent The content to be evaluated.
/// \param rName An arbitrary user defined name for referring to the content.
/// \returns the result as ValueObject.
ValueObject EvaluateContent( Content const &rContent, std::string const &rName ) override;
TEASCRIPT_COMPILE_MODE_INLINE ValueObject EvaluateContent( Content const &rContent, std::string const &rName ) override;

public:
/// The default Constructor constructs the engine with everything loaded and bootstrapped.
Expand All @@ -99,10 +113,10 @@ class Engine : public EngineBase
}

/// Constructs the engine with the specified config. Use the helper funcions from config namespace to simplify the configuration.
explicit Engine( config::eConfig const config, eMode const mode = eMode::Compile );
TEASCRIPT_COMPILE_MODE_INLINE explicit Engine( config::eConfig const config, eMode const mode = eMode::Compile );

/// Convenience constructor for specifying the loading level and the opt-out feature mask separately.
Engine( config::eConfig const level, unsigned int const opt_out )
TEASCRIPT_COMPILE_MODE_INLINE Engine( config::eConfig const level, unsigned int const opt_out )
: Engine( config::build( level, opt_out ) )
{
}
Expand All @@ -115,20 +129,20 @@ class Engine : public EngineBase

/// Resets the state of the context (+ parser and machine). Will do a fresh bootstrap of the CoreLibrary with the current saved configuration.
/// \note This should be done usually prior each execution of a script to not interfer with old variables/modified environment.
void ResetState() override;
TEASCRIPT_COMPILE_MODE_INLINE void ResetState() override;

/// enables or disables debug mode (default: off). This will also set the optimization level to Debug.
/// \note enabled debug mode will preserve the source code for the ASTNodes. Thus, the parsing will take slightly longer and the ASTNodes use more memory.
void SetDebugMode( bool const enabled ) noexcept;
TEASCRIPT_COMPILE_MODE_INLINE void SetDebugMode( bool const enabled ) noexcept;

/// Returns the stored variable with name \param rName starting search in the current scope up to toplevel scope.
/// \throw May throw exception::unknown_identifier or a different excection based on exception::eval_eror/runtime_error.
ValueObject GetVar( std::string const &rName ) const override;
TEASCRIPT_COMPILE_MODE_INLINE ValueObject GetVar( std::string const &rName ) const override;

/// Invokes the TeaScript function with name rName with parameters in rParams. (see EngineBase for the nice convenience function CallFuncEx!)
/// \returns the ValueObject result from the called fuction.
/// \throw May throw exception::unknown_identifier or a different excection based on exception::eval_eror/runtime_error.
ValueObject CallFunc( std::string const &rName, std::vector<ValueObject> &rParams ) override;
TEASCRIPT_COMPILE_MODE_INLINE ValueObject CallFunc( std::string const &rName, std::vector<ValueObject> &rParams ) override;


/// Registers the given callback function \param rCallback as name \param rName in the current scope.
Expand Down Expand Up @@ -162,7 +176,7 @@ class Engine : public EngineBase
/// \note The legacy form of the arg variables "arg1", "arg2", ... is available via the compile setting TEASCRIPT_ENGINE_USE_LEGACY_ARGS=1
/// \note \see EngineBase::ExecuteScript for further important details.
/// \throw May throw exception::load_file_error or any exception based on exception::parsing_error/compile_error/eval_error/runtime_error/bad_value_cast.
ValueObject ExecuteScript( std::filesystem::path const &path, std::vector<ValueObject> const &args );
TEASCRIPT_COMPILE_MODE_INLINE ValueObject ExecuteScript( std::filesystem::path const &path, std::vector<ValueObject> const &args );


/// Executes the given \param program in the TeaStackVM with the (optional) script parameters \param args.
Expand All @@ -172,16 +186,16 @@ class Engine : public EngineBase
/// \note The legacy form of the arg variables "arg1", "arg2", ... is available via the compile setting TEASCRIPT_ENGINE_USE_LEGACY_ARGS=1
/// \note \see EngineBase::ExecuteScript for further important details.
/// \throw May throw an exception based on exception::eval_error/runtime_error/bad_value_cast.
ValueObject ExecuteProgram( StackVM::ProgramPtr const &program, std::vector<ValueObject> const &args = {} );
TEASCRIPT_COMPILE_MODE_INLINE ValueObject ExecuteProgram( StackVM::ProgramPtr const &program, std::vector<ValueObject> const &args = {} );


/// Compiles the given \param rContent to a binary program for the TeaStackVM with the optimization level \param opt_level.
/// \throw May throw an exception based on exception::compile_error/eval_error/runtime_error.
StackVM::ProgramPtr CompileContent( Content const &rContent, eOptimize const opt_level = eOptimize::O0, std::string const &rName = "_USER_CODE_" );
TEASCRIPT_COMPILE_MODE_INLINE StackVM::ProgramPtr CompileContent( Content const &rContent, eOptimize const opt_level = eOptimize::O0, std::string const &rName = "_USER_CODE_" );

/// Compiles the script referenced with file path \param path to a binary program for the TeaStackVM with the optimization level \param opt_level.
/// \throw May throw exception::load_file_error or any exception based on exception::parsing_error/compile_error/eval_error/runtime_error.
StackVM::ProgramPtr CompileScript( std::filesystem::path const &path, eOptimize const opt_level = eOptimize::O0 );
TEASCRIPT_COMPILE_MODE_INLINE StackVM::ProgramPtr CompileScript( std::filesystem::path const &path, eOptimize const opt_level = eOptimize::O0 );

/// Compiles the TeaScript code in \param code to a binary program for the TeaStackVM with the optimization level \param opt_level.
/// \param name is arbitrary user defined name for referring to the code.
Expand Down Expand Up @@ -212,9 +226,6 @@ class Engine : public EngineBase

} // namespace teascript

#if !defined(TEASCRIPT_DISABLE_HEADER_ONLY)
# define TEASCRIPT_DISABLE_HEADER_ONLY 0
#endif

// check for broken header only / not header only configurations.
#if (0==TEASCRIPT_DISABLE_HEADER_ONLY) && defined(TEASCRIPT_INCLUDE_DEFINITIONS)
Expand Down
Loading

0 comments on commit 51977e6

Please sign in to comment.