File indexing completed on 2024-11-24 03:56:27
0001 /* 0002 * SPDX-FileCopyrightText: 2019-2023 Mattia Basaglia <dev@dragon.best> 0003 * 0004 * SPDX-License-Identifier: GPL-3.0-or-later 0005 */ 0006 0007 #pragma once 0008 0009 #include <stdexcept> 0010 #include <memory> 0011 0012 #include <QString> 0013 #include <QObject> 0014 #include <QHash> 0015 #include <QDir> 0016 #include <QVariant> 0017 0018 #include "app/qstring_exception.hpp" 0019 0020 0021 namespace app::scripting { 0022 0023 class ScriptError : public QStringException<>{ using Ctor::Ctor; }; 0024 0025 class ScriptEngine; 0026 0027 /** 0028 * \brief Context for running scripts (implementation) 0029 */ 0030 class ScriptExecutionContext : public QObject 0031 { 0032 Q_OBJECT 0033 public: 0034 0035 ScriptExecutionContext() = default; 0036 virtual ~ScriptExecutionContext() = default; 0037 0038 /** 0039 * \brief Exposes \p obj as a global variable called \p name 0040 */ 0041 virtual void expose(const QString& name, const QVariant& obj) = 0; 0042 0043 /** 0044 * \brief Evaluates \p code and serializes the result (if any) into a string 0045 * \throws ScriptError on errors with the script 0046 */ 0047 virtual QString eval_to_string(const QString& code) = 0; 0048 0049 /** 0050 * \brief Evaluates \p line and returns a strin list of available code completions 0051 */ 0052 virtual QStringList eval_completions(const QString& line) { Q_UNUSED(line); return {}; } 0053 0054 /** 0055 * \brief Marks an app module that must be loaded 0056 */ 0057 virtual void app_module(const QString& name) = 0; 0058 0059 /** 0060 * \brief Runs a function from a file 0061 * \return Whether the call was successful 0062 */ 0063 virtual bool run_from_module ( 0064 const QDir& path, ///< Path containing the module file 0065 const QString& module, ///< Module name to load, meaning of this depends on the engine 0066 const QString& function,///< Function to call 0067 const QVariantList& args///< Arguments to pass the function 0068 ) = 0; 0069 0070 /** 0071 * \brief Engine that created the context 0072 */ 0073 virtual const ScriptEngine* engine() const = 0; 0074 0075 Q_SIGNALS: 0076 void stderr_line(const QString&); 0077 void stdout_line(const QString&); 0078 0079 private: 0080 ScriptExecutionContext(const ScriptExecutionContext&) = delete; 0081 ScriptExecutionContext& operator=(const ScriptExecutionContext&) = delete; 0082 }; 0083 0084 0085 /** 0086 * \brief Script context holder 0087 */ 0088 using ScriptContext = std::unique_ptr<ScriptExecutionContext>; 0089 0090 0091 /** 0092 * \brief Scripting system metadata 0093 */ 0094 class ScriptEngine 0095 { 0096 public: 0097 virtual ~ScriptEngine() = default; 0098 0099 /** 0100 * \brief short machine-readable name for the language / engine 0101 */ 0102 virtual QString slug() const = 0; 0103 0104 /** 0105 * \brief Human-readable name 0106 */ 0107 virtual QString label() const = 0; 0108 0109 /** 0110 * \brief Creates an execution context to run scripts 0111 */ 0112 virtual ScriptContext create_context() const = 0; 0113 0114 protected: 0115 template<class T> 0116 struct Autoregister; 0117 }; 0118 0119 0120 class ScriptEngineFactory 0121 { 0122 public: 0123 static ScriptEngineFactory& instance() 0124 { 0125 static ScriptEngineFactory instance; 0126 return instance; 0127 } 0128 0129 void register_engine(std::unique_ptr<ScriptEngine> eng) 0130 { 0131 engines_.push_back(std::move(eng)); 0132 } 0133 0134 const ScriptEngine* engine(const QString& slug) const 0135 { 0136 for ( const auto& engine: engines_ ) 0137 if ( engine->slug() == slug ) 0138 return engine.get(); 0139 return nullptr; 0140 } 0141 0142 const std::vector<std::unique_ptr<ScriptEngine>>& engines() const 0143 { 0144 return engines_; 0145 } 0146 0147 private: 0148 ScriptEngineFactory() = default; 0149 ScriptEngineFactory(const ScriptEngineFactory&) = delete; 0150 ~ScriptEngineFactory() = default; 0151 std::vector<std::unique_ptr<ScriptEngine>> engines_; 0152 }; 0153 0154 template<class T> 0155 struct ScriptEngine::Autoregister 0156 { 0157 template<class... Args> 0158 Autoregister(Args&&... args) 0159 { 0160 ScriptEngineFactory::instance().register_engine(std::make_unique<T>(std::forward<Args>(args)...)); 0161 } 0162 }; 0163 0164 } // namespace app::scripting