diff --git a/include/scratchcpp/block.h b/include/scratchcpp/block.h index fcdf936f..33a3d5a2 100644 --- a/include/scratchcpp/block.h +++ b/include/scratchcpp/block.h @@ -23,7 +23,7 @@ class LIBSCRATCHCPP_EXPORT Block : public Entity public: friend class Engine; - Block(const std::string &id, const std::string &opcode); + Block(const std::string &id, const std::string &opcode, bool isMonitorBlock = false); Block(const Block &) = delete; CompilerValue *compile(Compiler *compiler); @@ -90,6 +90,8 @@ class LIBSCRATCHCPP_EXPORT Block : public Entity InputValue *topLevelReporterInfo(); + bool isMonitorBlock() const; + private: void updateInputMap(); void updateFieldMap(); diff --git a/include/scratchcpp/compiler.h b/include/scratchcpp/compiler.h index cc7db35a..7930b710 100644 --- a/include/scratchcpp/compiler.h +++ b/include/scratchcpp/compiler.h @@ -38,6 +38,13 @@ class LIBSCRATCHCPP_EXPORT Compiler Unknown }; + enum class CodeType + { + Script, + Reporter, + HatPredicate + }; + using ArgTypes = std::vector; using Args = std::vector; @@ -49,7 +56,7 @@ class LIBSCRATCHCPP_EXPORT Compiler Target *target() const; std::shared_ptr block() const; - std::shared_ptr compile(std::shared_ptr startBlock, bool isHatPredicate = false); + std::shared_ptr compile(std::shared_ptr startBlock, CodeType codeType = CodeType::Script); void preoptimize(); CompilerValue *addFunctionCall(const std::string &functionName, StaticType returnType = StaticType::Void, const ArgTypes &argTypes = {}, const Args &args = {}); diff --git a/include/scratchcpp/executablecode.h b/include/scratchcpp/executablecode.h index c55a4983..1c5706ad 100644 --- a/include/scratchcpp/executablecode.h +++ b/include/scratchcpp/executablecode.h @@ -11,6 +11,7 @@ namespace libscratchcpp class ExecutionContext; class Thread; +struct ValueData; /*! \brief The ExecutableCode class represents the code of a compiled Scratch script. */ class LIBSCRATCHCPP_EXPORT ExecutableCode @@ -21,6 +22,12 @@ class LIBSCRATCHCPP_EXPORT ExecutableCode /*! Runs the script until it finishes or yields. */ virtual void run(ExecutionContext *context) = 0; + /*! + * Runs the reporter and returns its return value. + * \note Make sure to call value_free() to free the value. + */ + virtual ValueData runReporter(ExecutionContext *context) = 0; + /*! Runs the hat predicate and returns its return value. */ virtual bool runPredicate(ExecutionContext *context) = 0; diff --git a/include/scratchcpp/imonitorhandler.h b/include/scratchcpp/imonitorhandler.h index 3b72d5f9..e96de1ab 100644 --- a/include/scratchcpp/imonitorhandler.h +++ b/include/scratchcpp/imonitorhandler.h @@ -17,8 +17,7 @@ class LIBSCRATCHCPP_EXPORT IMonitorHandler virtual void init(Monitor *monitor) = 0; - // TODO: Add onValueChanged() - // virtual void onValueChanged(const VirtualMachine *vm) = 0; + virtual void onValueChanged(const Value &value) = 0; virtual void onXChanged(int x) = 0; virtual void onYChanged(int y) = 0; virtual void onVisibleChanged(bool visible) = 0; diff --git a/include/scratchcpp/monitor.h b/include/scratchcpp/monitor.h index b85decb7..d0527702 100644 --- a/include/scratchcpp/monitor.h +++ b/include/scratchcpp/monitor.h @@ -56,8 +56,7 @@ class LIBSCRATCHCPP_EXPORT Monitor : public Entity const std::string &opcode() const; - // TODO: Add updateValue() - // void updateValue(const VirtualMachine *vm); + void updateValue(const Value &value); void setValueChangeFunction(MonitorChangeFunc f); void changeValue(const Value &newValue); diff --git a/include/scratchcpp/thread.h b/include/scratchcpp/thread.h index 994de694..7a61529f 100644 --- a/include/scratchcpp/thread.h +++ b/include/scratchcpp/thread.h @@ -2,7 +2,7 @@ #pragma once -#include "global.h" +#include "valuedata.h" #include "spimpl.h" namespace libscratchcpp @@ -27,6 +27,7 @@ class LIBSCRATCHCPP_EXPORT Thread Script *script() const; void run(); + ValueData runReporter(); bool runPredicate(); void kill(); void reset(); diff --git a/src/blocks/listblocks.cpp b/src/blocks/listblocks.cpp index 422faec2..fdd15241 100644 --- a/src/blocks/listblocks.cpp +++ b/src/blocks/listblocks.cpp @@ -3,6 +3,7 @@ #include #include #include +#include #include #include @@ -27,6 +28,8 @@ Rgb ListBlocks::color() const void ListBlocks::registerBlocks(IEngine *engine) { + // Blocks + engine->addCompileFunction(this, "data_listcontents", &compileListContents); engine->addCompileFunction(this, "data_addtolist", &compileAddToList); engine->addCompileFunction(this, "data_deleteoflist", &compileDeleteOfList); engine->addCompileFunction(this, "data_deletealloflist", &compileDeleteAllOfList); @@ -36,6 +39,24 @@ void ListBlocks::registerBlocks(IEngine *engine) engine->addCompileFunction(this, "data_itemnumoflist", &compileItemNumOfList); engine->addCompileFunction(this, "data_lengthoflist", &compileLengthOfList); engine->addCompileFunction(this, "data_listcontainsitem", &compileListContainsItem); + + // Monitor names + engine->addMonitorNameFunction(this, "data_listcontents", &listContentsMonitorName); +} + +CompilerValue *ListBlocks::compileListContents(Compiler *compiler) +{ + auto list = compiler->field("LIST")->valuePtr(); + assert(list); + + // If this block is used in a list monitor, return the list pointer + if (compiler->block()->isMonitorBlock()) { + // TODO: Refactor this + // List monitors expect a reference to the list + // We don't have a list reference type, so cast the pointer to number + return compiler->addConstValue((uintptr_t)list.get()); + } else + return compiler->addListContents(static_cast(list.get())); } CompilerValue *ListBlocks::compileAddToList(Compiler *compiler) @@ -194,3 +215,19 @@ CompilerValue *ListBlocks::compileListContainsItem(Compiler *compiler) return nullptr; } + +const std::string &ListBlocks::listContentsMonitorName(Block *block) +{ + static const std::string empty = ""; + auto field = block->fieldAt(block->findField("LIST")); + + if (!field) + return empty; + + List *list = dynamic_cast(field->valuePtr().get()); + + if (list) + return list->name(); + else + return empty; +} diff --git a/src/blocks/listblocks.h b/src/blocks/listblocks.h index 02dbd79c..b6bf6741 100644 --- a/src/blocks/listblocks.h +++ b/src/blocks/listblocks.h @@ -19,6 +19,7 @@ class ListBlocks : public IExtension void registerBlocks(IEngine *engine) override; private: + static CompilerValue *compileListContents(Compiler *compiler); static CompilerValue *compileAddToList(Compiler *compiler); static CompilerValue *getListIndex(Compiler *compiler, CompilerValue *input, List *list, CompilerValue *listSize); static CompilerValue *compileDeleteOfList(Compiler *compiler); @@ -29,6 +30,8 @@ class ListBlocks : public IExtension static CompilerValue *compileItemNumOfList(Compiler *compiler); static CompilerValue *compileLengthOfList(Compiler *compiler); static CompilerValue *compileListContainsItem(Compiler *compiler); + + static const std::string &listContentsMonitorName(Block *block); }; } // namespace libscratchcpp diff --git a/src/blocks/variableblocks.cpp b/src/blocks/variableblocks.cpp index f7600f7c..a0d9f1a9 100644 --- a/src/blocks/variableblocks.cpp +++ b/src/blocks/variableblocks.cpp @@ -3,6 +3,7 @@ #include #include #include +#include #include #include #include @@ -28,9 +29,16 @@ Rgb VariableBlocks::color() const void VariableBlocks::registerBlocks(IEngine *engine) { + // Blocks engine->addCompileFunction(this, "data_variable", &compileVariable); engine->addCompileFunction(this, "data_setvariableto", &compileSetVariableTo); engine->addCompileFunction(this, "data_changevariableby", &compileChangeVariableBy); + + // Monitor names + engine->addMonitorNameFunction(this, "data_variable", &variableMonitorName); + + // Monitor change functions + engine->addMonitorChangeFunction(this, "data_variable", &changeVariableMonitorValue); } CompilerValue *VariableBlocks::compileVariable(Compiler *compiler) @@ -70,3 +78,33 @@ CompilerValue *VariableBlocks::compileChangeVariableBy(Compiler *compiler) return nullptr; } + +const std::string &VariableBlocks::variableMonitorName(Block *block) +{ + static const std::string empty = ""; + + auto field = block->fieldAt(block->findField("VARIABLE")); + + if (!field) + return empty; + + Variable *var = dynamic_cast(field->valuePtr().get()); + + if (var) + return var->name(); + else + return empty; +} + +void VariableBlocks::changeVariableMonitorValue(Block *block, const Value &newValue) +{ + auto field = block->fieldAt(block->findField("VARIABLE")); + + if (!field) + return; + + Variable *var = dynamic_cast(field->valuePtr().get()); + + if (var) + var->setValue(newValue); +} diff --git a/src/blocks/variableblocks.h b/src/blocks/variableblocks.h index 423085d3..bbae27d8 100644 --- a/src/blocks/variableblocks.h +++ b/src/blocks/variableblocks.h @@ -20,6 +20,9 @@ class VariableBlocks : public IExtension static CompilerValue *compileVariable(Compiler *compiler); static CompilerValue *compileSetVariableTo(Compiler *compiler); static CompilerValue *compileChangeVariableBy(Compiler *compiler); + + static const std::string &variableMonitorName(Block *block); + static void changeVariableMonitorValue(Block *block, const Value &newValue); }; } // namespace libscratchcpp diff --git a/src/engine/compiler.cpp b/src/engine/compiler.cpp index 17ea93ce..9fee9506 100644 --- a/src/engine/compiler.cpp +++ b/src/engine/compiler.cpp @@ -44,7 +44,7 @@ std::shared_ptr Compiler::block() const } /*! Compiles the script starting with the given block. */ -std::shared_ptr Compiler::compile(std::shared_ptr startBlock, bool isHatPredicate) +std::shared_ptr Compiler::compile(std::shared_ptr startBlock, CodeType codeType) { BlockPrototype *procedurePrototype = nullptr; @@ -60,14 +60,14 @@ std::shared_ptr Compiler::compile(std::shared_ptr startBl } } - impl->builder = impl->builderFactory->create(impl->ctx, procedurePrototype, isHatPredicate); + impl->builder = impl->builderFactory->create(impl->ctx, procedurePrototype, codeType); impl->substackTree.clear(); impl->substackHit = false; impl->emptySubstack = false; impl->warp = false; impl->block = startBlock; - if (impl->block && isHatPredicate) { + if (impl->block && codeType == CodeType::HatPredicate) { auto f = impl->block->hatPredicateCompileFunction(); if (f) { diff --git a/src/engine/internal/codebuilderfactory.cpp b/src/engine/internal/codebuilderfactory.cpp index 42ea1122..853f283c 100644 --- a/src/engine/internal/codebuilderfactory.cpp +++ b/src/engine/internal/codebuilderfactory.cpp @@ -13,10 +13,10 @@ std::shared_ptr CodeBuilderFactory::instance() return m_instance; } -std::shared_ptr CodeBuilderFactory::create(CompilerContext *ctx, BlockPrototype *procedurePrototype, bool isPredicate) const +std::shared_ptr CodeBuilderFactory::create(CompilerContext *ctx, BlockPrototype *procedurePrototype, Compiler::CodeType codeType) const { assert(dynamic_cast(ctx)); - return std::make_shared(static_cast(ctx), procedurePrototype, isPredicate); + return std::make_shared(static_cast(ctx), procedurePrototype, codeType); } std::shared_ptr CodeBuilderFactory::createCtx(IEngine *engine, Target *target) const diff --git a/src/engine/internal/codebuilderfactory.h b/src/engine/internal/codebuilderfactory.h index c0c29568..4dac2db7 100644 --- a/src/engine/internal/codebuilderfactory.h +++ b/src/engine/internal/codebuilderfactory.h @@ -11,7 +11,7 @@ class CodeBuilderFactory : public ICodeBuilderFactory { public: static std::shared_ptr instance(); - std::shared_ptr create(CompilerContext *ctx, BlockPrototype *procedurePrototype, bool isPredicate) const override; + std::shared_ptr create(CompilerContext *ctx, BlockPrototype *procedurePrototype, Compiler::CodeType codeType) const override; std::shared_ptr createCtx(IEngine *engine, Target *target) const override; private: diff --git a/src/engine/internal/engine.cpp b/src/engine/internal/engine.cpp index 4c42540a..54d0715a 100644 --- a/src/engine/internal/engine.cpp +++ b/src/engine/internal/engine.cpp @@ -276,7 +276,7 @@ void Engine::compile() script->setCode(compiler.compile(block)); if (block->hatPredicateCompileFunction()) - script->setHatPredicateCode(compiler.compile(block, true)); + script->setHatPredicateCode(compiler.compile(block, Compiler::CodeType::HatPredicate)); } else { std::cout << "warning: unsupported top level block: " << block->opcode() << std::endl; m_unsupportedBlocks.insert(block->opcode()); @@ -468,10 +468,10 @@ void Engine::updateMonitors() auto script = monitor->script(); if (script) { - // TODO: Implement this - /*auto thread = script->start(); - thread->run(); - monitor->updateValue(thread->vm());*/ + auto thread = script->start(); + ValueData value = thread->runReporter(); + monitor->updateValue(Value(value)); + value_free(&value); } } } @@ -1851,7 +1851,35 @@ int Engine::resolveFieldValue(IExtension *extension, const std::string &value) c void Engine::compileMonitor(std::shared_ptr monitor) { - // TODO: Implement this + Target *target = monitor->sprite() ? static_cast(monitor->sprite()) : stage(); + auto block = monitor->block(); + auto ext = blockExtension(block->opcode()); + + if (ext) { + auto ctx = Compiler::createContext(this, target); + Compiler compiler(ctx.get()); + MonitorNameFunc nameFunc = resolveMonitorNameFunc(ext, block->opcode()); + + if (nameFunc) + monitor->setName(nameFunc(block.get())); + + MonitorChangeFunc changeFunc = resolveMonitorChangeFunc(ext, block->opcode()); + monitor->setValueChangeFunction(changeFunc); + + auto script = std::make_shared