diff --git a/.github/workflows/utests-llvm.yml b/.github/workflows/utests-llvm.yml deleted file mode 100644 index d7d1ace8..00000000 --- a/.github/workflows/utests-llvm.yml +++ /dev/null @@ -1,28 +0,0 @@ -name: Unit tests (LLVM compiler) - -on: - push: - branches: '*' - pull_request: - branches: [ "master" ] - -env: - BUILD_TYPE: Debug - -jobs: - build: - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v3 - with: - submodules: true - - - name: Configure CMake - run: cmake -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DLIBSCRATCHCPP_BUILD_UNIT_TESTS=ON -DLIBSCRATCHCPP_USE_LLVM=ON - - - name: Build - run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}} -j$(nproc --all) - - - name: Run unit tests - run: ctest --test-dir build -V diff --git a/.github/workflows/utests-minimal.yml b/.github/workflows/utests-minimal.yml index 00e02639..41573123 100644 --- a/.github/workflows/utests-minimal.yml +++ b/.github/workflows/utests-minimal.yml @@ -19,7 +19,7 @@ jobs: submodules: true - name: Configure CMake - run: cmake -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DLIBSCRATCHCPP_BUILD_UNIT_TESTS=ON -DLIBSCRATCHCPP_AUDIO_SUPPORT=OFF -DLIBSCRATCHCPP_NETWORK_SUPPORT=OFF -DLIBSCRATCHCPP_COMPUTED_GOTO=OFF + run: cmake -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DLIBSCRATCHCPP_BUILD_UNIT_TESTS=ON -DLIBSCRATCHCPP_AUDIO_SUPPORT=OFF -DLIBSCRATCHCPP_NETWORK_SUPPORT=OFF - name: Build run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}} -j$(nproc --all) diff --git a/CMakeLists.txt b/CMakeLists.txt index 7bb3d052..16be0cca 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -8,24 +8,13 @@ set(CMAKE_CXX_STANDARD_REQUIRED ON) option(LIBSCRATCHCPP_BUILD_UNIT_TESTS "Build unit tests" ON) option(LIBSCRATCHCPP_NETWORK_SUPPORT "Support for downloading projects" ON) -option(LIBSCRATCHCPP_COMPUTED_GOTO "Support for computed goto" ON) -option(LIBSCRATCHCPP_USE_LLVM "Compile scripts to LLVM IR (work in progress)" OFF) option(LIBSCRATCHCPP_PRINT_LLVM_IR "Print LLVM IR of compiled Scratch scripts (for debugging)" OFF) -if (NOT (CMAKE_CXX_COMPILER_ID STREQUAL "GNU")) - # Computed goto not supported on anything except GCC - set(LIBSCRATCHCPP_COMPUTED_GOTO OFF CACHE BOOL "" FORCE) -endif() - add_library(scratchcpp SHARED) add_subdirectory(src) include_directories(src) # TODO: Remove this line include_directories(include) -if (LIBSCRATCHCPP_COMPUTED_GOTO) - target_compile_definitions(scratchcpp PRIVATE ENABLE_COMPUTED_GOTO) -endif() - target_sources(scratchcpp PUBLIC include/scratchcpp/global.h @@ -33,6 +22,14 @@ target_sources(scratchcpp include/scratchcpp/scratchconfiguration.h include/scratchcpp/iengine.h include/scratchcpp/iextension.h + include/scratchcpp/compiler.h + include/scratchcpp/compilercontext.h + include/scratchcpp/compilervalue.h + include/scratchcpp/compilerconstant.h + include/scratchcpp/compilerlocalvariable.h + include/scratchcpp/executablecode.h + include/scratchcpp/executioncontext.h + include/scratchcpp/promise.h include/scratchcpp/thread.h include/scratchcpp/asset.h include/scratchcpp/costume.h @@ -48,7 +45,6 @@ target_sources(scratchcpp include/scratchcpp/field.h include/scratchcpp/script.h include/scratchcpp/broadcast.h - include/scratchcpp/virtualmachine.h include/scratchcpp/blockprototype.h include/scratchcpp/block.h include/scratchcpp/istagehandler.h @@ -67,31 +63,11 @@ target_sources(scratchcpp include/scratchcpp/comment.h include/scratchcpp/monitor.h include/scratchcpp/imonitorhandler.h + include/scratchcpp/test/scriptbuilder.h ) -if (LIBSCRATCHCPP_USE_LLVM) - target_compile_definitions(scratchcpp PUBLIC USE_LLVM) - target_sources(scratchcpp - PUBLIC - include/scratchcpp/dev/compiler.h - include/scratchcpp/dev/compilercontext.h - include/scratchcpp/dev/compilervalue.h - include/scratchcpp/dev/compilerconstant.h - include/scratchcpp/dev/compilerlocalvariable.h - include/scratchcpp/dev/executablecode.h - include/scratchcpp/dev/executioncontext.h - include/scratchcpp/dev/promise.h - include/scratchcpp/dev/test/scriptbuilder.h - ) - - if(LIBSCRATCHCPP_PRINT_LLVM_IR) - target_compile_definitions(scratchcpp PRIVATE PRINT_LLVM_IR) - endif() -else() - target_sources(scratchcpp - PUBLIC - include/scratchcpp/compiler.h - ) +if(LIBSCRATCHCPP_PRINT_LLVM_IR) + target_compile_definitions(scratchcpp PRIVATE PRINT_LLVM_IR) endif() include(FetchContent) @@ -123,11 +99,9 @@ if (LIBSCRATCHCPP_NETWORK_SUPPORT) target_compile_definitions(scratchcpp PRIVATE LIBSCRATCHCPP_NETWORK_SUPPORT) endif() -if (LIBSCRATCHCPP_USE_LLVM) - include(build/HunterPackages.cmake) - include(build/LLVM.cmake) - target_link_libraries(scratchcpp PRIVATE LLVM) -endif() +include(build/HunterPackages.cmake) +include(build/LLVM.cmake) +target_link_libraries(scratchcpp PRIVATE LLVM) target_compile_definitions(scratchcpp PRIVATE LIBSCRATCHCPP_LIBRARY) target_compile_definitions(scratchcpp PRIVATE LIBSCRATCHCPP_VERSION="${PROJECT_VERSION}") diff --git a/include/scratchcpp/block.h b/include/scratchcpp/block.h index 76c70dc1..fcdf936f 100644 --- a/include/scratchcpp/block.h +++ b/include/scratchcpp/block.h @@ -10,9 +10,7 @@ namespace libscratchcpp class IEngine; class Target; -#ifdef USE_LLVM class CompilerValue; -#endif class Input; class Field; class Comment; @@ -28,11 +26,7 @@ class LIBSCRATCHCPP_EXPORT Block : public Entity Block(const std::string &id, const std::string &opcode); Block(const Block &) = delete; -#ifdef USE_LLVM CompilerValue *compile(Compiler *compiler); -#else - void compile(Compiler *compiler); -#endif const std::string &opcode() const; diff --git a/include/scratchcpp/compiler.h b/include/scratchcpp/compiler.h index 05ab0c7c..82f4d5bb 100644 --- a/include/scratchcpp/compiler.h +++ b/include/scratchcpp/compiler.h @@ -2,89 +2,149 @@ #pragma once -#include -#include #include +#include #include "global.h" #include "spimpl.h" -#include "virtualmachine.h" -#include "value.h" namespace libscratchcpp { +class CompilerContext; class IEngine; -class Block; -class Input; -class InputValue; -class Field; +class Target; +class ExecutableCode; +class CompilerValue; +class CompilerConstant; +class CompilerLocalVariable; class Variable; class List; +class Input; +class Field; class BlockPrototype; -class Entity; class CompilerPrivate; -/*! \brief The Compiler class provides an API for compiling scripts of targets to bytecode. */ +/*! \brief The Compiler class provides API for compiling Scratch scripts. */ class LIBSCRATCHCPP_EXPORT Compiler { public: - enum class SubstackType + enum class StaticType { - Loop, - IfStatement + Void, + Number, + Bool, + String, + Unknown }; - Compiler(IEngine *engine, Target *target = nullptr); - Compiler(const Compiler &) = delete; - - void init(); - void compile(std::shared_ptr topLevelBlock); - void end(); + using ArgTypes = std::vector; + using Args = std::vector; - const std::vector &bytecode() const; - const std::vector &hatPredicateBytecode() const; + Compiler(CompilerContext *ctx); + Compiler(IEngine *engine, Target *target); + Compiler(const Compiler &) = delete; IEngine *engine() const; Target *target() const; - - const std::vector &constInputValues() const; - std::vector constValues() const; - - const std::vector &variables() const; - std::vector variablePtrs() const; - - const std::vector &lists() const; - - void addInstruction(vm::Opcode opcode, const std::initializer_list &args = {}); - void addInput(Input *input); - void addInput(int id); - void addConstValue(const Value &value); - void addFunctionCall(BlockFunc f); - void addProcedureArg(const std::string &procCode, const std::string &argName); - void moveToSubstack(std::shared_ptr substack1, std::shared_ptr substack2, SubstackType type); - void moveToSubstack(std::shared_ptr substack, SubstackType type); + std::shared_ptr block() const; + + std::shared_ptr compile(std::shared_ptr startBlock); + void preoptimize(); + + CompilerValue *addFunctionCall(const std::string &functionName, StaticType returnType = StaticType::Void, const ArgTypes &argTypes = {}, const Args &args = {}); + CompilerValue *addTargetFunctionCall(const std::string &functionName, StaticType returnType = StaticType::Void, const ArgTypes &argTypes = {}, const Args &args = {}); + CompilerValue *addFunctionCallWithCtx(const std::string &functionName, StaticType returnType = StaticType::Void, const ArgTypes &argTypes = {}, const Args &args = {}); + CompilerConstant *addConstValue(const Value &value); + CompilerValue *addLoopIndex(); + CompilerValue *addLocalVariableValue(CompilerLocalVariable *variable); + CompilerValue *addVariableValue(Variable *variable); + CompilerValue *addListContents(List *list); + CompilerValue *addListItem(List *list, CompilerValue *index); + CompilerValue *addListItemIndex(List *list, CompilerValue *item); + CompilerValue *addListContains(List *list, CompilerValue *item); + CompilerValue *addListSize(List *list); + CompilerValue *addProcedureArgument(const std::string &name); + + CompilerValue *addInput(const std::string &name); + CompilerValue *addInput(Input *input); + + CompilerValue *createAdd(CompilerValue *operand1, CompilerValue *operand2); + CompilerValue *createSub(CompilerValue *operand1, CompilerValue *operand2); + CompilerValue *createMul(CompilerValue *operand1, CompilerValue *operand2); + CompilerValue *createDiv(CompilerValue *operand1, CompilerValue *operand2); + + CompilerValue *createRandom(CompilerValue *from, CompilerValue *to); + CompilerValue *createRandomInt(CompilerValue *from, CompilerValue *to); + + CompilerValue *createCmpEQ(CompilerValue *operand1, CompilerValue *operand2); + CompilerValue *createCmpGT(CompilerValue *operand1, CompilerValue *operand2); + CompilerValue *createCmpLT(CompilerValue *operand1, CompilerValue *operand2); + + CompilerValue *createStrCmpEQ(CompilerValue *string1, CompilerValue *string2, bool caseSensitive = false); + + CompilerValue *createAnd(CompilerValue *operand1, CompilerValue *operand2); + CompilerValue *createOr(CompilerValue *operand1, CompilerValue *operand2); + CompilerValue *createNot(CompilerValue *operand); + + CompilerValue *createMod(CompilerValue *num1, CompilerValue *num2); + CompilerValue *createRound(CompilerValue *num); + CompilerValue *createAbs(CompilerValue *num); + CompilerValue *createFloor(CompilerValue *num); + CompilerValue *createCeil(CompilerValue *num); + CompilerValue *createSqrt(CompilerValue *num); + CompilerValue *createSin(CompilerValue *num); + CompilerValue *createCos(CompilerValue *num); + CompilerValue *createTan(CompilerValue *num); + CompilerValue *createAsin(CompilerValue *num); + CompilerValue *createAcos(CompilerValue *num); + CompilerValue *createAtan(CompilerValue *num); + CompilerValue *createLn(CompilerValue *num); + CompilerValue *createLog10(CompilerValue *num); + CompilerValue *createExp(CompilerValue *num); + CompilerValue *createExp10(CompilerValue *num); + + CompilerValue *createSelect(CompilerValue *cond, CompilerValue *trueValue, CompilerValue *falseValue, Compiler::StaticType valueType); + + CompilerLocalVariable *createLocalVariable(Compiler::StaticType type); + void createLocalVariableWrite(CompilerLocalVariable *variable, CompilerValue *value); + + void createVariableWrite(Variable *variable, CompilerValue *value); + + void createListClear(List *list); + void createListRemove(List *list, CompilerValue *index); + void createListAppend(List *list, CompilerValue *item); + void createListInsert(List *list, CompilerValue *index, CompilerValue *item); + void createListReplace(List *list, CompilerValue *index, CompilerValue *item); + + void beginIfStatement(CompilerValue *cond); + void beginElseBranch(); + void endIf(); + + void beginWhileLoop(CompilerValue *cond); + void beginRepeatUntilLoop(CompilerValue *cond); + void beginLoopCondition(); + void endLoop(); + + void moveToIf(CompilerValue *cond, std::shared_ptr substack); + void moveToIfElse(CompilerValue *cond, std::shared_ptr substack1, std::shared_ptr substack2); + void moveToRepeatLoop(CompilerValue *count, std::shared_ptr substack); + void moveToWhileLoop(CompilerValue *cond, std::shared_ptr substack); + void moveToRepeatUntilLoop(CompilerValue *cond, std::shared_ptr substack); void warp(); - Input *input(int id) const; - Field *field(int id) const; - std::shared_ptr inputBlock(int id) const; - unsigned int variableIndex(std::shared_ptr varEntity); - unsigned int listIndex(std::shared_ptr listEntity); - unsigned int constIndex(InputValue *value); - unsigned int procedureIndex(const std::string &proc); - long procedureArgIndex(const std::string &procCode, const std::string &argName); - - const std::vector &procedures() const; + void createYield(); + void createStop(); - const std::shared_ptr &block() const; - void setBlock(std::shared_ptr block); + void createProcedureCall(BlockPrototype *prototype, const Compiler::Args &args); - BlockPrototype *procedurePrototype() const; - void setProcedurePrototype(BlockPrototype *prototype); + Input *input(const std::string &name) const; + Field *field(const std::string &name) const; const std::unordered_set &unsupportedBlocks() const; + static std::shared_ptr createContext(IEngine *engine, Target *target); + private: spimpl::unique_impl_ptr impl; }; diff --git a/include/scratchcpp/dev/compilerconstant.h b/include/scratchcpp/compilerconstant.h similarity index 100% rename from include/scratchcpp/dev/compilerconstant.h rename to include/scratchcpp/compilerconstant.h diff --git a/include/scratchcpp/dev/compilercontext.h b/include/scratchcpp/compilercontext.h similarity index 94% rename from include/scratchcpp/dev/compilercontext.h rename to include/scratchcpp/compilercontext.h index 8038c5fe..1b335199 100644 --- a/include/scratchcpp/dev/compilercontext.h +++ b/include/scratchcpp/compilercontext.h @@ -2,8 +2,8 @@ #pragma once -#include "../global.h" -#include "../spimpl.h" +#include "global.h" +#include "spimpl.h" namespace libscratchcpp { diff --git a/include/scratchcpp/dev/compilerlocalvariable.h b/include/scratchcpp/compilerlocalvariable.h similarity index 100% rename from include/scratchcpp/dev/compilerlocalvariable.h rename to include/scratchcpp/compilerlocalvariable.h diff --git a/include/scratchcpp/dev/compilervalue.h b/include/scratchcpp/compilervalue.h similarity index 100% rename from include/scratchcpp/dev/compilervalue.h rename to include/scratchcpp/compilervalue.h diff --git a/include/scratchcpp/dev/compiler.h b/include/scratchcpp/dev/compiler.h deleted file mode 100644 index db7ada83..00000000 --- a/include/scratchcpp/dev/compiler.h +++ /dev/null @@ -1,152 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 - -#pragma once - -#include -#include - -#include "../global.h" -#include "../spimpl.h" - -namespace libscratchcpp -{ - -class CompilerContext; -class IEngine; -class Target; -class ExecutableCode; -class CompilerValue; -class CompilerConstant; -class CompilerLocalVariable; -class Variable; -class List; -class Input; -class Field; -class BlockPrototype; -class CompilerPrivate; - -/*! \brief The Compiler class provides API for compiling Scratch scripts. */ -class LIBSCRATCHCPP_EXPORT Compiler -{ - public: - enum class StaticType - { - Void, - Number, - Bool, - String, - Unknown - }; - - using ArgTypes = std::vector; - using Args = std::vector; - - Compiler(CompilerContext *ctx); - Compiler(IEngine *engine, Target *target); - Compiler(const Compiler &) = delete; - - IEngine *engine() const; - Target *target() const; - std::shared_ptr block() const; - - std::shared_ptr compile(std::shared_ptr startBlock); - void preoptimize(); - - CompilerValue *addFunctionCall(const std::string &functionName, StaticType returnType = StaticType::Void, const ArgTypes &argTypes = {}, const Args &args = {}); - CompilerValue *addTargetFunctionCall(const std::string &functionName, StaticType returnType = StaticType::Void, const ArgTypes &argTypes = {}, const Args &args = {}); - CompilerValue *addFunctionCallWithCtx(const std::string &functionName, StaticType returnType = StaticType::Void, const ArgTypes &argTypes = {}, const Args &args = {}); - CompilerConstant *addConstValue(const Value &value); - CompilerValue *addLoopIndex(); - CompilerValue *addLocalVariableValue(CompilerLocalVariable *variable); - CompilerValue *addVariableValue(Variable *variable); - CompilerValue *addListContents(List *list); - CompilerValue *addListItem(List *list, CompilerValue *index); - CompilerValue *addListItemIndex(List *list, CompilerValue *item); - CompilerValue *addListContains(List *list, CompilerValue *item); - CompilerValue *addListSize(List *list); - CompilerValue *addProcedureArgument(const std::string &name); - - CompilerValue *addInput(const std::string &name); - CompilerValue *addInput(Input *input); - - CompilerValue *createAdd(CompilerValue *operand1, CompilerValue *operand2); - CompilerValue *createSub(CompilerValue *operand1, CompilerValue *operand2); - CompilerValue *createMul(CompilerValue *operand1, CompilerValue *operand2); - CompilerValue *createDiv(CompilerValue *operand1, CompilerValue *operand2); - - CompilerValue *createRandom(CompilerValue *from, CompilerValue *to); - CompilerValue *createRandomInt(CompilerValue *from, CompilerValue *to); - - CompilerValue *createCmpEQ(CompilerValue *operand1, CompilerValue *operand2); - CompilerValue *createCmpGT(CompilerValue *operand1, CompilerValue *operand2); - CompilerValue *createCmpLT(CompilerValue *operand1, CompilerValue *operand2); - - CompilerValue *createStrCmpEQ(CompilerValue *string1, CompilerValue *string2, bool caseSensitive = false); - - CompilerValue *createAnd(CompilerValue *operand1, CompilerValue *operand2); - CompilerValue *createOr(CompilerValue *operand1, CompilerValue *operand2); - CompilerValue *createNot(CompilerValue *operand); - - CompilerValue *createMod(CompilerValue *num1, CompilerValue *num2); - CompilerValue *createRound(CompilerValue *num); - CompilerValue *createAbs(CompilerValue *num); - CompilerValue *createFloor(CompilerValue *num); - CompilerValue *createCeil(CompilerValue *num); - CompilerValue *createSqrt(CompilerValue *num); - CompilerValue *createSin(CompilerValue *num); - CompilerValue *createCos(CompilerValue *num); - CompilerValue *createTan(CompilerValue *num); - CompilerValue *createAsin(CompilerValue *num); - CompilerValue *createAcos(CompilerValue *num); - CompilerValue *createAtan(CompilerValue *num); - CompilerValue *createLn(CompilerValue *num); - CompilerValue *createLog10(CompilerValue *num); - CompilerValue *createExp(CompilerValue *num); - CompilerValue *createExp10(CompilerValue *num); - - CompilerValue *createSelect(CompilerValue *cond, CompilerValue *trueValue, CompilerValue *falseValue, Compiler::StaticType valueType); - - CompilerLocalVariable *createLocalVariable(Compiler::StaticType type); - void createLocalVariableWrite(CompilerLocalVariable *variable, CompilerValue *value); - - void createVariableWrite(Variable *variable, CompilerValue *value); - - void createListClear(List *list); - void createListRemove(List *list, CompilerValue *index); - void createListAppend(List *list, CompilerValue *item); - void createListInsert(List *list, CompilerValue *index, CompilerValue *item); - void createListReplace(List *list, CompilerValue *index, CompilerValue *item); - - void beginIfStatement(CompilerValue *cond); - void beginElseBranch(); - void endIf(); - - void beginWhileLoop(CompilerValue *cond); - void beginRepeatUntilLoop(CompilerValue *cond); - void beginLoopCondition(); - void endLoop(); - - void moveToIf(CompilerValue *cond, std::shared_ptr substack); - void moveToIfElse(CompilerValue *cond, std::shared_ptr substack1, std::shared_ptr substack2); - void moveToRepeatLoop(CompilerValue *count, std::shared_ptr substack); - void moveToWhileLoop(CompilerValue *cond, std::shared_ptr substack); - void moveToRepeatUntilLoop(CompilerValue *cond, std::shared_ptr substack); - void warp(); - - void createYield(); - void createStop(); - - void createProcedureCall(BlockPrototype *prototype, const Compiler::Args &args); - - Input *input(const std::string &name) const; - Field *field(const std::string &name) const; - - const std::unordered_set &unsupportedBlocks() const; - - static std::shared_ptr createContext(IEngine *engine, Target *target); - - private: - spimpl::unique_impl_ptr impl; -}; - -} // namespace libscratchcpp diff --git a/include/scratchcpp/dev/executablecode.h b/include/scratchcpp/executablecode.h similarity index 97% rename from include/scratchcpp/dev/executablecode.h rename to include/scratchcpp/executablecode.h index 8136e40e..094d0181 100644 --- a/include/scratchcpp/dev/executablecode.h +++ b/include/scratchcpp/executablecode.h @@ -4,7 +4,7 @@ #include -#include "../global.h" +#include "global.h" namespace libscratchcpp { diff --git a/include/scratchcpp/dev/executioncontext.h b/include/scratchcpp/executioncontext.h similarity index 95% rename from include/scratchcpp/dev/executioncontext.h rename to include/scratchcpp/executioncontext.h index 69d11945..23f6ce73 100644 --- a/include/scratchcpp/dev/executioncontext.h +++ b/include/scratchcpp/executioncontext.h @@ -2,8 +2,8 @@ #pragma once -#include "../global.h" -#include "../spimpl.h" +#include "global.h" +#include "spimpl.h" namespace libscratchcpp { diff --git a/include/scratchcpp/global.h b/include/scratchcpp/global.h index e5824008..d1b6de97 100644 --- a/include/scratchcpp/global.h +++ b/include/scratchcpp/global.h @@ -23,36 +23,17 @@ namespace libscratchcpp { -class VirtualMachine; class Compiler; -#ifdef USE_LLVM class CompilerValue; -#endif class Block; class Value; -/*! - * \typedef BlockFunc - * - * BlockFunc is a function pointer for block implementation functions. - */ -using BlockFunc = unsigned int (*)(VirtualMachine *vm); - -#ifdef USE_LLVM /*! * \typedef BlockComp * * BlockComp is a function pointer for functions which are used to compile blocks. */ using BlockComp = CompilerValue *(*)(Compiler *); -#else -/*! - * \typedef BlockComp - * - * BlockComp is a function pointer for functions which are used to compile blocks to bytecode. - */ -using BlockComp = void (*)(Compiler *); -#endif // USE_LLVM /*! * \typedef MonitorNameFunc @@ -68,21 +49,12 @@ using MonitorNameFunc = const std::string &(*)(Block *); */ using MonitorChangeFunc = void (*)(Block *, const Value &newValue); -#ifdef USE_LLVM /*! * \typedef HatPredicateCompileFunc * * HatPredicateCompileFunc is a function pointer for functions which are used to compile edge-activated hat predicates. */ using HatPredicateCompileFunc = CompilerValue *(*)(Compiler *vm); -#else -/*! - * \typedef HatPredicateCompileFunc - * - * HatPredicateCompileFunc is a function pointer for functions which are used to compile edge-activated hat predicates to bytecode. - */ -using HatPredicateCompileFunc = void (*)(Compiler *vm); -#endif // USE_LLVM } // namespace libscratchcpp diff --git a/include/scratchcpp/iengine.h b/include/scratchcpp/iengine.h index a8cffbc2..196ed443 100644 --- a/include/scratchcpp/iengine.h +++ b/include/scratchcpp/iengine.h @@ -6,7 +6,6 @@ #include #include #include -#include #include "global.h" #include "signal.h" @@ -219,12 +218,6 @@ class LIBSCRATCHCPP_EXPORT IEngine /*! Returns the timer of the project. */ virtual ITimer *timer() const = 0; - /*! Returns the index of the given block function. */ - virtual unsigned int functionIndex(BlockFunc f) = 0; - - /*! Returns the list of block functions. */ - virtual const std::vector &blockFunctions() const = 0; - /*! * Call this from IExtension#registerBlocks() to add a compile function to a block section. * \see Extensions diff --git a/include/scratchcpp/imonitorhandler.h b/include/scratchcpp/imonitorhandler.h index d31582ba..3b72d5f9 100644 --- a/include/scratchcpp/imonitorhandler.h +++ b/include/scratchcpp/imonitorhandler.h @@ -17,7 +17,8 @@ class LIBSCRATCHCPP_EXPORT IMonitorHandler virtual void init(Monitor *monitor) = 0; - virtual void onValueChanged(const VirtualMachine *vm) = 0; + // TODO: Add onValueChanged() + // virtual void onValueChanged(const VirtualMachine *vm) = 0; virtual void onXChanged(int x) = 0; virtual void onYChanged(int y) = 0; virtual void onVisibleChanged(bool visible) = 0; diff --git a/include/scratchcpp/inputvalue.h b/include/scratchcpp/inputvalue.h index f1580c75..d973e702 100644 --- a/include/scratchcpp/inputvalue.h +++ b/include/scratchcpp/inputvalue.h @@ -10,9 +10,7 @@ namespace libscratchcpp { -#ifdef USE_LLVM class CompilerValue; -#endif class Block; class Entity; class InputValuePrivate; @@ -37,11 +35,7 @@ class LIBSCRATCHCPP_EXPORT InputValue InputValue(Type type = Type::Number); -#ifdef USE_LLVM CompilerValue *compile(Compiler *compiler); -#else - void compile(Compiler *compiler); -#endif Type type() const; void setType(Type newType); diff --git a/include/scratchcpp/monitor.h b/include/scratchcpp/monitor.h index 85f5c8e4..b85decb7 100644 --- a/include/scratchcpp/monitor.h +++ b/include/scratchcpp/monitor.h @@ -56,7 +56,8 @@ class LIBSCRATCHCPP_EXPORT Monitor : public Entity const std::string &opcode() const; - void updateValue(const VirtualMachine *vm); + // TODO: Add updateValue() + // void updateValue(const VirtualMachine *vm); void setValueChangeFunction(MonitorChangeFunc f); void changeValue(const Value &newValue); diff --git a/include/scratchcpp/dev/promise.h b/include/scratchcpp/promise.h similarity index 91% rename from include/scratchcpp/dev/promise.h rename to include/scratchcpp/promise.h index 2c0b6010..304139b4 100644 --- a/include/scratchcpp/dev/promise.h +++ b/include/scratchcpp/promise.h @@ -2,8 +2,8 @@ #pragma once -#include "../global.h" -#include "../spimpl.h" +#include "global.h" +#include "spimpl.h" namespace libscratchcpp { diff --git a/include/scratchcpp/script.h b/include/scratchcpp/script.h index 895aaaf7..3032cd4c 100644 --- a/include/scratchcpp/script.h +++ b/include/scratchcpp/script.h @@ -14,13 +14,8 @@ namespace libscratchcpp class Target; class Block; class IEngine; -#ifdef USE_LLVM class ExecutableCode; -#endif -class Value; class Thread; -class Variable; -class List; class ScriptPrivate; /*! \brief The Script class represents a compiled Scratch script. */ @@ -33,29 +28,15 @@ class LIBSCRATCHCPP_EXPORT Script Target *target() const; std::shared_ptr topBlock() const; - unsigned int *bytecode() const; - const std::vector &bytecodeVector() const; - void setBytecode(const std::vector &code); - -#ifdef USE_LLVM ExecutableCode *code() const; void setCode(std::shared_ptr code); -#endif - void setHatPredicateBytecode(const std::vector &code); bool runHatPredicate(Target *target); - void setProcedures(const std::vector &procedures); - void setConstValues(const std::vector &values); - void setVariables(const std::vector &variables); - void setLists(const std::vector &lists); - std::shared_ptr start(); std::shared_ptr start(Target *target); private: - BlockFunc *getFunctions() const; - spimpl::unique_impl_ptr impl; }; diff --git a/include/scratchcpp/dev/test/scriptbuilder.h b/include/scratchcpp/test/scriptbuilder.h similarity index 98% rename from include/scratchcpp/dev/test/scriptbuilder.h rename to include/scratchcpp/test/scriptbuilder.h index a01db6b8..bbacae68 100644 --- a/include/scratchcpp/dev/test/scriptbuilder.h +++ b/include/scratchcpp/test/scriptbuilder.h @@ -4,7 +4,7 @@ #include -#include "../../inputvalue.h" +#include "../inputvalue.h" namespace libscratchcpp { diff --git a/include/scratchcpp/thread.h b/include/scratchcpp/thread.h index b5936db7..8708b47c 100644 --- a/include/scratchcpp/thread.h +++ b/include/scratchcpp/thread.h @@ -8,11 +8,8 @@ namespace libscratchcpp { -class VirtualMachine; class Target; -#ifdef USE_LLVM class Promise; -#endif class IEngine; class Script; class ThreadPrivate; @@ -24,7 +21,6 @@ class LIBSCRATCHCPP_EXPORT Thread Thread(Target *target, IEngine *engine, Script *script); Thread(const Thread &) = delete; - VirtualMachine *vm() const; Target *target() const; IEngine *engine() const; Script *script() const; @@ -35,13 +31,8 @@ class LIBSCRATCHCPP_EXPORT Thread bool isFinished() const; -#ifdef USE_LLVM std::shared_ptr promise() const; void setPromise(std::shared_ptr promise); -#else - void promise(); - void resolvePromise(); -#endif private: spimpl::unique_impl_ptr impl; diff --git a/include/scratchcpp/virtualmachine.h b/include/scratchcpp/virtualmachine.h deleted file mode 100644 index e72aef4f..00000000 --- a/include/scratchcpp/virtualmachine.h +++ /dev/null @@ -1,149 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 - -#pragma once - -#include -#include - -#include "spimpl.h" - -namespace libscratchcpp -{ - -class VirtualMachinePrivate; - -/*! \brief A namespace for global variables and enumerations of the VirtualMachine. */ -namespace vm -{ - -enum Opcode -{ - OP_START, /*!< The first instruction of every bytecode. */ - OP_HALT, /*!< The last instruction of every bytecode. */ - OP_CONST, /*!< Adds a constant value with the index in the argument to the next register. */ - OP_NULL, /*!< Adds a null (zero) value to the next register. */ - OP_CHECKPOINT, /*!< A checkpoint for the VirtualMachine::moveToLastCheckpoint() method. */ - OP_IF, /*!< Jumps to the next instruction if the last register holds "true". If it's false, jumps to OP_ELSE or OP_ENDIF. */ - OP_ELSE, /*!< Jumps to OP_ENDIF. This instruction is typically reached when the if statement condition was "true". */ - OP_ENDIF, /*!< Doesn't do anything, but is used by OP_IF and OP_ELSE. */ - OP_FOREVER_LOOP, /*!< Runs a forever loop. */ - OP_REPEAT_LOOP, /*!< Runs a repeat loop with the number of periods stored in the last register. */ - OP_REPEAT_LOOP_INDEX, /*!< Returns the index of current repeat loop. */ - OP_REPEAT_LOOP_INDEX1, /*!< Returns the index of current repeat loop plus 1. */ - OP_UNTIL_LOOP, /*!< Evaluates the condition before OP_BEGIN_UNTIL_LOOP and runs a repeat until loop. */ - OP_BEGIN_UNTIL_LOOP, /*!< Used by OP_UNTIL_LOOP. */ - OP_LOOP_END, /*!< Ends the loop. */ - OP_PRINT, /*!< Prints the value stored in the last register. */ - OP_ADD, /*!< Adds the values stored in the last 2 registers and stores the result in the last registry, deleting the input registers. */ - OP_SUBTRACT, /*!< Subtracts the values stored in the last 2 registers and stores the result in the last registry, deleting the input registers. */ - OP_MULTIPLY, /*!< Multiplies the values stored in the last 2 registers and stores the result in the last registry, deleting the input registers. */ - OP_DIVIDE, /*!< Divides the values stored in the last 2 registers and stores the result in the last registry, deleting the input registers. */ - OP_MOD, /*!< Calculates module of the values stored in the last 2 registers and stores the result in the last registry, deleting the input registers. */ - OP_RANDOM, /*!< Generates a random value in the range stored in the last 2 registers and stores the result in the last registry, deleting the input registers. */ - OP_ROUND, /*!< Rounds the number in the last register and stores the result in the last register. */ - OP_ABS, /*!< Calculates the absolute value of the number in the last register and stores the result in the last register. */ - OP_FLOOR, /*!< Calculates the floor of the number in the last register and stores the result in the last register. */ - OP_CEIL, /*!< Calculates the ceiling of the number in the last register and stores the result in the last register. */ - OP_SQRT, /*!< Calculates the square root of the number in the last register and stores the result in the last register. */ - OP_SIN, /*!< Calculates the sine of the number in the last register and stores the result in the last register. */ - OP_COS, /*!< Calculates the cosine of the number in the last register and stores the result in the last register. */ - OP_TAN, /*!< Calculates the tangent of the number in the last register and stores the result in the last register. */ - OP_ASIN, /*!< Calculates the arcsine of the number in the last register and stores the result in the last register. */ - OP_ACOS, /*!< Calculates the arccosine of the number in the last register and stores the result in the last register. */ - OP_ATAN, /*!< Calculates the arctangent of the number in the last register and stores the result in the last register. */ - OP_GREATER_THAN, /*!< Compares (>) the values stored in the last 2 registers and stores the result in the last registry, deleting the input registers. */ - OP_LESS_THAN, /*!< Compares (<) the values stored in the last 2 registers and stores the result in the last registry, deleting the input registers. */ - OP_EQUALS, /*!< Compares (==) the values stored in the last 2 registers and stores the result in the last registry, deleting the input registers. */ - OP_AND, /*!< AND gate. The result is stored in the last registry and the input registers are deleted. */ - OP_OR, /*!< OR gate. The result is stored in the last registry and the input registers are deleted. */ - OP_NOT, /*!< NOT gate. The result is stored in the last registry and the input registers are deleted. */ - OP_SET_VAR, /*!< Sets the variable with the index in the argument to the value stored in the last register. */ - OP_CHANGE_VAR, /*!< Increments (or decrements) the variable with the index in the argument by the value stored in the last register. */ - OP_READ_VAR, /*!< Reads the variable with the index in the argument and stores the value in the last register. */ - OP_READ_LIST, /*!< Converts the list with the index in the argument to a string and stores the result in the last register. */ - OP_LIST_APPEND, /*!< Appens the value stored in the last register to the list with the index in the argument. */ - OP_LIST_DEL, /*!< Deletes the index (or item like "last" or "random") stored in the last register of the list with the index in the argument. */ - OP_LIST_DEL_ALL, /*!< Clears the list with the index in the argument. */ - OP_LIST_INSERT, /*!< Inserts the value from the second last register at the index (or item like "last" or "random") stored in the last register to the list with the index in the argument. */ - OP_LIST_REPLACE, /*!< Replaces the index (or item like "last" or "random") stored in the second last register with the value from the last register in the list with the index in the argument. */ - OP_LIST_GET_ITEM, /*!< Stores the value at the index (or item like "last" or "random") (of the list with the index in the argument) stored in the last register, in the last register. */ - OP_LIST_INDEX_OF, /*!< Stores the index of the value from the last register in the last register (of the list with the index in the argument). */ - OP_LIST_LENGTH, /*!< Stores the length of the list with the index in the argument, in the last register. */ - OP_LIST_CONTAINS, /*!< Stores true in the last register if the list with the index in the argument contains the value from the last register. */ - OP_STR_CONCAT, /*!< Concatenates the strings stored in the last 2 registers and stores the result in the last register, deleting the input registers. */ - OP_STR_AT, /*!< Stores the character at index in the last register of the string in the second last register, in the last register. */ - OP_STR_LENGTH, /*!< Stores the length of the string in the last register, in the last register. */ - OP_STR_CONTAINS, /*!< Stores true in the last register if the string stored in the second last register contains the substring in the last register. */ - OP_EXEC, /*!< Calls the function with the index in the argument. */ - OP_INIT_PROCEDURE, /*!< Initializes the list of procedure (custom block) arguments. */ - OP_CALL_PROCEDURE, /*!< Calls the procedure (custom block) with the index in the argument. */ - OP_ADD_ARG, /*!< Adds a procedure (custom block) argument with the value from the last register. */ - OP_READ_ARG, /*!< Reads the procedure (custom block) argument with the index in the argument and stores the value in the last register. */ - OP_BREAK_FRAME, /*!< Breaks current frame at the end of the loop. */ - OP_WARP /*!< Runs the script without screen refresh. */ -}; - -} - -class Target; -class IEngine; -class Script; -class Thread; -class List; - -/*! \brief The VirtualMachine class is a virtual machine for compiled Scratch scripts. */ -class LIBSCRATCHCPP_EXPORT VirtualMachine -{ - public: - VirtualMachine(); - VirtualMachine(Target *target, IEngine *engine, Script *script, Thread *thread = nullptr); - VirtualMachine(const VirtualMachine &) = delete; - - void setProcedures(unsigned int **procedures); - void setFunctions(BlockFunc *functions); - void setConstValues(const Value *values); - void setVariables(Value **variables); - void setLists(List **lists); - void setVariablesVector(const std::vector &variables); - void setListsVector(const std::vector &lists); - - void setBytecode(unsigned int *code); - - unsigned int **procedures() const; - const BlockFunc *functions() const; - const Value *constValues() const; - Value **variables() const; - List **lists() const; - - unsigned int *bytecode() const; - - size_t registerCount() const; - - Target *target() const; - IEngine *engine() const; - Script *script() const; - Thread *thread() const; - - const Value *getInput(unsigned int index, unsigned int argCount) const; - - void addReturnValue(const Value &v); - void replaceReturnValue(const Value &v, unsigned int offset); - - void run(); - void kill(); - void reset(); - void moveToLastCheckpoint(); - - void stop(bool savePos = true, bool breakFrame = false, bool goBack = false); - void promise(); - void resolvePromise(); - - bool atEnd() const; - - bool savePos() const; - - private: - spimpl::unique_impl_ptr impl; -}; - -} // namespace libscratchcpp diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index f4e1fcc2..0d63d5d7 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -11,15 +11,9 @@ target_sources(scratchcpp rect_p.h ) -if (NOT LIBSCRATCHCPP_USE_LLVM) - add_subdirectory(blocks) -endif() - +add_subdirectory(blocks) add_subdirectory(engine) add_subdirectory(internal) add_subdirectory(scratch) add_subdirectory(audio) - -if (LIBSCRATCHCPP_USE_LLVM) - add_subdirectory(dev) -endif() +add_subdirectory(test) diff --git a/src/blocks/blocks.cpp b/src/blocks/blocks.cpp index a9d12f1e..c352547b 100644 --- a/src/blocks/blocks.cpp +++ b/src/blocks/blocks.cpp @@ -5,34 +5,34 @@ #include "blocks.h" #ifdef ENABLE_MOTION_BLOCKS -#include "blocks/motionblocks.h" +#include "motionblocks.h" #endif #ifdef ENABLE_LOOKS_BLOCKS -#include "blocks/looksblocks.h" +#include "looksblocks.h" #endif #ifdef ENABLE_SOUND_BLOCKS -#include "blocks/soundblocks.h" +#include "soundblocks.h" #endif #ifdef ENABLE_EVENT_BLOCKS -#include "blocks/eventblocks.h" +#include "eventblocks.h" #endif #ifdef ENABLE_CONTROL_BLOCKS -#include "blocks/controlblocks.h" +#include "controlblocks.h" #endif #ifdef ENABLE_SENSING_BLOCKS -#include "blocks/sensingblocks.h" +#include "sensingblocks.h" #endif #ifdef ENABLE_OPERATOR_BLOCKS -#include "blocks/operatorblocks.h" +#include "operatorblocks.h" #endif #ifdef ENABLE_VARIABLE_BLOCKS -#include "blocks/variableblocks.h" +#include "variableblocks.h" #endif #ifdef ENABLE_LIST_BLOCKS -#include "blocks/listblocks.h" +#include "listblocks.h" #endif #ifdef ENABLE_CUSTOM_BLOCKS -#include "blocks/customblocks.h" +#include "customblocks.h" #endif using namespace libscratchcpp; diff --git a/src/blocks/controlblocks.cpp b/src/blocks/controlblocks.cpp index ab5aab69..6e8f3d1b 100644 --- a/src/blocks/controlblocks.cpp +++ b/src/blocks/controlblocks.cpp @@ -2,19 +2,20 @@ #include #include -#include +#include +#include #include #include -#include -#include +#include +#include +#include +#include +#include #include "controlblocks.h" -#include "../engine/internal/clock.h" using namespace libscratchcpp; -IClock *ControlBlocks::clock = nullptr; - std::string ControlBlocks::name() const { return "Control"; @@ -27,276 +28,206 @@ std::string ControlBlocks::description() const void ControlBlocks::registerBlocks(IEngine *engine) { - // Blocks - engine->addCompileFunction(this, "control_forever", &compileRepeatForever); + engine->addCompileFunction(this, "control_forever", &compileForever); engine->addCompileFunction(this, "control_repeat", &compileRepeat); - engine->addCompileFunction(this, "control_repeat_until", &compileRepeatUntil); - engine->addCompileFunction(this, "control_while", &compileRepeatWhile); - engine->addCompileFunction(this, "control_for_each", &compileRepeatForEach); - engine->addCompileFunction(this, "control_if", &compileIfStatement); - engine->addCompileFunction(this, "control_if_else", &compileIfElseStatement); + engine->addCompileFunction(this, "control_if", &compileIf); + engine->addCompileFunction(this, "control_if_else", &compileIfElse); engine->addCompileFunction(this, "control_stop", &compileStop); engine->addCompileFunction(this, "control_wait", &compileWait); engine->addCompileFunction(this, "control_wait_until", &compileWaitUntil); + engine->addCompileFunction(this, "control_repeat_until", &compileRepeatUntil); + engine->addCompileFunction(this, "control_while", &compileWhile); + engine->addCompileFunction(this, "control_for_each", &compileForEach); engine->addCompileFunction(this, "control_start_as_clone", &compileStartAsClone); - engine->addCompileFunction(this, "control_create_clone_of", &compileCreateClone); + engine->addCompileFunction(this, "control_create_clone_of", &compileCreateCloneOf); engine->addCompileFunction(this, "control_delete_this_clone", &compileDeleteThisClone); - - // Inputs - engine->addInput(this, "SUBSTACK", SUBSTACK); - engine->addInput(this, "SUBSTACK2", SUBSTACK2); - engine->addInput(this, "TIMES", TIMES); - engine->addInput(this, "CONDITION", CONDITION); - engine->addInput(this, "DURATION", DURATION); - engine->addInput(this, "VALUE", VALUE); - engine->addInput(this, "CLONE_OPTION", CLONE_OPTION); - - // Fields - engine->addField(this, "STOP_OPTION", STOP_OPTION); - engine->addField(this, "VARIABLE", VARIABLE); - - // Field values - engine->addFieldValue(this, "all", StopAll); - engine->addFieldValue(this, "this script", StopThisScript); - engine->addFieldValue(this, "other scripts in sprite", StopOtherScriptsInSprite); - engine->addFieldValue(this, "other scripts in stage", StopOtherScriptsInSprite); } -void ControlBlocks::compileRepeatForever(Compiler *compiler) +CompilerValue *ControlBlocks::compileForever(Compiler *compiler) { - compiler->addInstruction(vm::OP_FOREVER_LOOP); - compiler->moveToSubstack(compiler->inputBlock(SUBSTACK), Compiler::SubstackType::Loop); + auto substack = compiler->input("SUBSTACK"); + compiler->beginLoopCondition(); + compiler->moveToWhileLoop(compiler->addConstValue(true), substack ? substack->valueBlock() : nullptr); + return nullptr; } -void ControlBlocks::compileRepeat(Compiler *compiler) +CompilerValue *ControlBlocks::compileRepeat(Compiler *compiler) { - auto substack = compiler->inputBlock(SUBSTACK); - if (substack) { - compiler->addInput(TIMES); - compiler->addInstruction(vm::OP_REPEAT_LOOP); - compiler->moveToSubstack(substack, Compiler::SubstackType::Loop); - } + auto substack = compiler->input("SUBSTACK"); + compiler->moveToRepeatLoop(compiler->addInput("TIMES"), substack ? substack->valueBlock() : nullptr); + return nullptr; } -void ControlBlocks::compileRepeatUntil(Compiler *compiler) +CompilerValue *ControlBlocks::compileIf(Compiler *compiler) { - auto substack = compiler->inputBlock(SUBSTACK); - compiler->addInstruction(vm::OP_UNTIL_LOOP); - compiler->addInput(CONDITION); - compiler->addInstruction(vm::OP_BEGIN_UNTIL_LOOP); - compiler->moveToSubstack(substack, Compiler::SubstackType::Loop); + auto substack = compiler->input("SUBSTACK"); + compiler->moveToIf(compiler->addInput("CONDITION"), substack ? substack->valueBlock() : nullptr); + return nullptr; } -void ControlBlocks::compileRepeatWhile(Compiler *compiler) +CompilerValue *ControlBlocks::compileIfElse(Compiler *compiler) { - auto substack = compiler->inputBlock(SUBSTACK); - compiler->addInstruction(vm::OP_UNTIL_LOOP); - compiler->addInput(CONDITION); - compiler->addInstruction(vm::OP_NOT); - compiler->addInstruction(vm::OP_BEGIN_UNTIL_LOOP); - compiler->moveToSubstack(substack, Compiler::SubstackType::Loop); + auto substack = compiler->input("SUBSTACK"); + auto substack2 = compiler->input("SUBSTACK2"); + compiler->moveToIfElse(compiler->addInput("CONDITION"), substack ? substack->valueBlock() : nullptr, substack2 ? substack2->valueBlock() : nullptr); + return nullptr; } -void ControlBlocks::compileRepeatForEach(Compiler *compiler) +CompilerValue *ControlBlocks::compileStop(Compiler *compiler) { - compiler->addInput(VALUE); - auto substack = compiler->inputBlock(SUBSTACK); - if (substack) { - compiler->addInstruction(vm::OP_REPEAT_LOOP); - compiler->addInstruction(vm::OP_REPEAT_LOOP_INDEX1); - compiler->addInstruction(vm::OP_SET_VAR, { compiler->variableIndex(compiler->field(VARIABLE)->valuePtr()) }); - compiler->moveToSubstack(substack, Compiler::SubstackType::Loop); - } else - compiler->addInstruction(vm::OP_SET_VAR, { compiler->variableIndex(compiler->field(VARIABLE)->valuePtr()) }); + Field *option = compiler->field("STOP_OPTION"); + + if (option) { + std::string str = option->value().toString(); + + if (str == "all") + compiler->addFunctionCallWithCtx("control_stop_all", Compiler::StaticType::Void); + else if (str == "this script") + compiler->createStop(); + else if (str == "other scripts in sprite" || str == "other scripts in stage") + compiler->addFunctionCallWithCtx("control_stop_other_scripts_in_target", Compiler::StaticType::Void); + } + + return nullptr; } -void ControlBlocks::compileIfStatement(Compiler *compiler) +CompilerValue *ControlBlocks::compileWait(Compiler *compiler) { - auto substack = compiler->inputBlock(SUBSTACK); - if (substack) { - compiler->addInput(CONDITION); - compiler->addInstruction(vm::OP_IF); - compiler->moveToSubstack(substack, Compiler::SubstackType::IfStatement); - } + auto duration = compiler->addInput("DURATION"); + compiler->addFunctionCallWithCtx("control_start_wait", Compiler::StaticType::Void, { Compiler::StaticType::Number }, { duration }); + compiler->createYield(); + + compiler->beginLoopCondition(); + auto elapsed = compiler->addFunctionCallWithCtx("control_stack_timer_elapsed", Compiler::StaticType::Bool); + compiler->beginRepeatUntilLoop(elapsed); + compiler->endLoop(); + + return nullptr; } -void ControlBlocks::compileIfElseStatement(Compiler *compiler) +CompilerValue *ControlBlocks::compileWaitUntil(Compiler *compiler) { - auto substack1 = compiler->inputBlock(SUBSTACK); - auto substack2 = compiler->inputBlock(SUBSTACK2); - if (substack1 || substack2) - compiler->addInput(CONDITION); - if (substack1 && substack2) { - compiler->addInstruction(vm::OP_IF); - compiler->moveToSubstack(substack1, substack2, Compiler::SubstackType::IfStatement); - } else if (substack1) { - compiler->addInstruction(vm::OP_IF); - compiler->moveToSubstack(substack1, Compiler::SubstackType::IfStatement); - } else if (substack2) { - compiler->addInstruction(vm::OP_NOT); - compiler->addInstruction(vm::OP_IF); - compiler->moveToSubstack(substack2, Compiler::SubstackType::IfStatement); - } + compiler->beginLoopCondition(); + compiler->beginRepeatUntilLoop(compiler->addInput("CONDITION")); + compiler->endLoop(); + return nullptr; } -void ControlBlocks::compileStop(Compiler *compiler) +CompilerValue *ControlBlocks::compileRepeatUntil(Compiler *compiler) { - int option = compiler->field(STOP_OPTION)->specialValueId(); - switch (option) { - case StopAll: - compiler->addFunctionCall(&stopAll); - compiler->addInstruction(vm::OP_HALT); - break; - - case StopThisScript: - compiler->addInstruction(vm::OP_HALT); - break; - - case StopOtherScriptsInSprite: - compiler->addFunctionCall(&stopOtherScriptsInSprite); - break; - } + auto substack = compiler->input("SUBSTACK"); + compiler->beginLoopCondition(); + compiler->moveToRepeatUntilLoop(compiler->addInput("CONDITION"), substack ? substack->valueBlock() : nullptr); + return nullptr; } -void ControlBlocks::compileWait(Compiler *compiler) +CompilerValue *ControlBlocks::compileWhile(Compiler *compiler) { - compiler->addInput(DURATION); - compiler->addFunctionCall(&startWait); - compiler->addFunctionCall(&wait); + auto substack = compiler->input("SUBSTACK"); + compiler->beginLoopCondition(); + compiler->moveToWhileLoop(compiler->addInput("CONDITION"), substack ? substack->valueBlock() : nullptr); + return nullptr; } -void ControlBlocks::compileWaitUntil(Compiler *compiler) +CompilerValue *ControlBlocks::compileForEach(Compiler *compiler) { - compiler->addInstruction(vm::OP_CHECKPOINT); - compiler->addInput(CONDITION); - compiler->addFunctionCall(&waitUntil); + Variable *var = static_cast(compiler->field("VARIABLE")->valuePtr().get()); + assert(var); + auto substack = compiler->input("SUBSTACK"); + compiler->moveToRepeatLoop(compiler->addInput("VALUE"), substack ? substack->valueBlock() : nullptr); + auto index = compiler->createAdd(compiler->addLoopIndex(), compiler->addConstValue(1)); + compiler->createVariableWrite(var, index); + return nullptr; } -void ControlBlocks::compileStartAsClone(Compiler *compiler) +CompilerValue *ControlBlocks::compileStartAsClone(Compiler *compiler) { compiler->engine()->addCloneInitScript(compiler->block()); + return nullptr; } -void ControlBlocks::compileCreateClone(Compiler *compiler) +CompilerValue *ControlBlocks::compileCreateCloneOf(Compiler *compiler) { - Input *input = compiler->input(CLONE_OPTION); + Input *input = compiler->input("CLONE_OPTION"); if (input->pointsToDropdownMenu()) { std::string spriteName = input->selectedMenuItem(); if (spriteName == "_myself_") - compiler->addFunctionCall(&createCloneOfMyself); + compiler->addTargetFunctionCall("control_create_clone_of_myself"); else { - int index = compiler->engine()->findTarget(spriteName); - compiler->addConstValue(index); - compiler->addFunctionCall(&createCloneByIndex); + auto index = compiler->engine()->findTarget(spriteName); + CompilerValue *arg = compiler->addConstValue(index); + compiler->addFunctionCallWithCtx("control_create_clone_by_index", Compiler::StaticType::Void, { Compiler::StaticType::Number }, { arg }); } } else { - compiler->addInput(input); - compiler->addFunctionCall(&createClone); + CompilerValue *arg = compiler->addInput("CLONE_OPTION"); + compiler->addFunctionCallWithCtx("control_create_clone", Compiler::StaticType::Void, { Compiler::StaticType::String }, { arg }); } -} -void ControlBlocks::compileDeleteThisClone(Compiler *compiler) -{ - compiler->addFunctionCall(&deleteThisClone); + return nullptr; } -unsigned int ControlBlocks::stopAll(VirtualMachine *vm) +CompilerValue *ControlBlocks::compileDeleteThisClone(Compiler *compiler) { - vm->engine()->stop(); - return 0; + compiler->addTargetFunctionCall("control_delete_this_clone"); + return nullptr; } -unsigned int ControlBlocks::stopOtherScriptsInSprite(VirtualMachine *vm) +extern "C" void control_stop_all(ExecutionContext *ctx) { - vm->engine()->stopTarget(vm->target(), vm->thread()); - return 0; + ctx->engine()->stop(); } -unsigned int ControlBlocks::startWait(VirtualMachine *vm) +extern "C" void control_stop_other_scripts_in_target(ExecutionContext *ctx) { - if (!clock) - clock = Clock::instance().get(); - - auto currentTime = clock->currentSteadyTime(); - m_timeMap[vm] = { currentTime, vm->getInput(0, 1)->toDouble() * 1000 }; - vm->engine()->requestRedraw(); - - return 1; + Thread *thread = ctx->thread(); + ctx->engine()->stopTarget(thread->target(), thread); } -unsigned int ControlBlocks::wait(VirtualMachine *vm) +extern "C" void control_start_wait(ExecutionContext *ctx, double seconds) { - if (!clock) - clock = Clock::instance().get(); - - auto currentTime = clock->currentSteadyTime(); - assert(m_timeMap.count(vm) == 1); - if (std::chrono::duration_cast(currentTime - m_timeMap[vm].first).count() >= m_timeMap[vm].second) { - m_timeMap.erase(vm); - vm->stop(true, true, false); - } else - vm->stop(true, true, true); - return 0; + ctx->stackTimer()->start(seconds); + ctx->engine()->requestRedraw(); } -unsigned int ControlBlocks::waitUntil(VirtualMachine *vm) +extern "C" bool control_stack_timer_elapsed(ExecutionContext *ctx) { - if (!vm->getInput(0, 1)->toBool()) { - vm->moveToLastCheckpoint(); - vm->stop(true, true, false); - } - return 1; + return ctx->stackTimer()->elapsed(); } -unsigned int ControlBlocks::createClone(VirtualMachine *vm) +extern "C" void control_create_clone_of_myself(Target *target) { - std::string spriteName = vm->getInput(0, 1)->toString(); - Target *target; - - if (spriteName == "_myself_") - target = vm->target(); - else - target = vm->engine()->targetAt(vm->engine()->findTarget(spriteName)); - - Sprite *sprite = dynamic_cast(target); - - if (sprite) - sprite->clone(); - - return 1; + if (!target->isStage()) + static_cast(target)->clone(); } -unsigned int ControlBlocks::createCloneByIndex(VirtualMachine *vm) +extern "C" void control_create_clone_by_index(ExecutionContext *ctx, double index) { - Target *target = vm->engine()->targetAt(vm->getInput(0, 1)->toInt()); - Sprite *sprite = dynamic_cast(target); + Target *target = ctx->engine()->targetAt(index); - if (sprite) - sprite->clone(); - - return 1; + if (!target->isStage()) + static_cast(target)->clone(); } -unsigned int ControlBlocks::createCloneOfMyself(VirtualMachine *vm) +extern "C" void control_create_clone(ExecutionContext *ctx, const char *spriteName) { - Sprite *sprite = dynamic_cast(vm->target()); - - if (sprite) - sprite->clone(); + if (strcmp(spriteName, "_myself_") == 0) + control_create_clone_of_myself(ctx->thread()->target()); + else { + IEngine *engine = ctx->engine(); + auto index = engine->findTarget(spriteName); + Target *target = engine->targetAt(index); - return 0; + if (!target->isStage()) + static_cast(target)->clone(); + } } -unsigned int ControlBlocks::deleteThisClone(VirtualMachine *vm) +extern "C" void control_delete_this_clone(Target *target) { - Sprite *sprite = dynamic_cast(vm->target()); - - if (sprite && sprite->isClone()) { - vm->engine()->stopTarget(sprite, nullptr); - sprite->deleteClone(); + if (!target->isStage()) { + target->engine()->stopTarget(target, nullptr); + static_cast(target)->deleteClone(); } - - return 0; } diff --git a/src/blocks/controlblocks.h b/src/blocks/controlblocks.h index aa0c8710..17850824 100644 --- a/src/blocks/controlblocks.h +++ b/src/blocks/controlblocks.h @@ -3,76 +3,32 @@ #pragma once #include -#include -#include namespace libscratchcpp { -class Compiler; -class VirtualMachine; -class IClock; - -/*! \brief The ControlBlocks class contains the implementation of control blocks. */ class ControlBlocks : public IExtension { public: - enum Inputs - { - SUBSTACK, - SUBSTACK2, - TIMES, - CONDITION, - DURATION, - VALUE, - CLONE_OPTION - }; - - enum Fields - { - STOP_OPTION, - VARIABLE - }; - - enum FieldValues - { - StopAll, - StopThisScript, - StopOtherScriptsInSprite - }; - std::string name() const override; std::string description() const override; void registerBlocks(IEngine *engine) override; - static void compileRepeatForever(Compiler *compiler); - static void compileRepeat(Compiler *compiler); - static void compileRepeatUntil(Compiler *compiler); - static void compileRepeatWhile(Compiler *compiler); - static void compileRepeatForEach(Compiler *compiler); - static void compileIfStatement(Compiler *compiler); - static void compileIfElseStatement(Compiler *compiler); - static void compileStop(Compiler *compiler); - static void compileWait(Compiler *compiler); - static void compileWaitUntil(Compiler *compiler); - static void compileStartAsClone(Compiler *compiler); - static void compileCreateClone(Compiler *compiler); - static void compileDeleteThisClone(Compiler *compiler); - - static unsigned int stopAll(VirtualMachine *vm); - static unsigned int stopOtherScriptsInSprite(VirtualMachine *vm); - static unsigned int startWait(VirtualMachine *vm); - static unsigned int wait(VirtualMachine *vm); - static unsigned int waitUntil(VirtualMachine *vm); - static unsigned int createClone(VirtualMachine *vm); - static unsigned int createCloneByIndex(VirtualMachine *vm); - static unsigned int createCloneOfMyself(VirtualMachine *vm); - static unsigned int deleteThisClone(VirtualMachine *vm); - - static inline std::unordered_map> m_timeMap; - - static IClock *clock; + private: + static CompilerValue *compileForever(Compiler *compiler); + static CompilerValue *compileRepeat(Compiler *compiler); + static CompilerValue *compileIf(Compiler *compiler); + static CompilerValue *compileIfElse(Compiler *compiler); + static CompilerValue *compileStop(Compiler *compiler); + static CompilerValue *compileWait(Compiler *compiler); + static CompilerValue *compileWaitUntil(Compiler *compiler); + static CompilerValue *compileRepeatUntil(Compiler *compiler); + static CompilerValue *compileWhile(Compiler *compiler); + static CompilerValue *compileForEach(Compiler *compiler); + static CompilerValue *compileStartAsClone(Compiler *compiler); + static CompilerValue *compileCreateCloneOf(Compiler *compiler); + static CompilerValue *compileDeleteThisClone(Compiler *compiler); }; } // namespace libscratchcpp diff --git a/src/blocks/customblocks.cpp b/src/blocks/customblocks.cpp index aee3b757..961c510c 100644 --- a/src/blocks/customblocks.cpp +++ b/src/blocks/customblocks.cpp @@ -2,9 +2,9 @@ #include #include -#include -#include +#include #include +#include #include "customblocks.h" @@ -22,60 +22,34 @@ std::string CustomBlocks::description() const void CustomBlocks::registerBlocks(IEngine *engine) { - // Blocks - engine->addCompileFunction(this, "procedures_definition", &compileDefinition); + engine->addCompileFunction(this, "procedures_definition", [](Compiler *) -> CompilerValue * { return nullptr; }); engine->addCompileFunction(this, "procedures_call", &compileCall); engine->addCompileFunction(this, "argument_reporter_boolean", &compileArgument); engine->addCompileFunction(this, "argument_reporter_string_number", &compileArgument); - - // Inputs - engine->addInput(this, "custom_block", CUSTOM_BLOCK); - - // Fields - engine->addField(this, "VALUE", VALUE); -} - -void CustomBlocks::compileDefinition(Compiler *compiler) -{ - auto prototype = compiler->input(CUSTOM_BLOCK)->valueBlock()->mutationPrototype(); - compiler->setProcedurePrototype(prototype); - if (prototype->warp()) - compiler->warp(); - const std::vector &args = prototype->argumentNames(); - const std::string &code = prototype->procCode(); - for (const std::string &arg : args) - compiler->addProcedureArg(code, arg); } -void CustomBlocks::compileCall(Compiler *compiler) +CompilerValue *CustomBlocks::compileCall(Compiler *compiler) { - compiler->addInstruction(vm::OP_INIT_PROCEDURE); auto block = compiler->block(); auto prototype = block->mutationPrototype(); - const std::vector &args = prototype->argumentIds(); - size_t i = 0; - for (const std::string &id : args) { - auto index = block->findInput(id); + const std::vector &procedureArgs = prototype->argumentIds(); + Compiler::Args args; + + for (size_t i = 0; i < procedureArgs.size(); i++) { + auto index = block->findInput(procedureArgs[i]); + if (index == -1) - compiler->addInstruction(vm::OP_NULL); + args.push_back(compiler->addConstValue(Value())); else - compiler->addInput(block->inputAt(index).get()); - compiler->addInstruction(vm::OP_ADD_ARG); - i++; + args.push_back(compiler->addInput(block->inputAt(index).get())); } - const std::string &code = prototype->procCode(); - compiler->addInstruction(vm::OP_CALL_PROCEDURE, { compiler->procedureIndex(code) }); + + compiler->createProcedureCall(compiler->block()->mutationPrototype(), args); + return nullptr; } -void CustomBlocks::compileArgument(Compiler *compiler) +CompilerValue *CustomBlocks::compileArgument(Compiler *compiler) { - if (compiler->procedurePrototype()) { - const std::string &argName = compiler->field(VALUE)->value().toString(); - auto index = compiler->procedureArgIndex(compiler->procedurePrototype()->procCode(), argName); - if (index == -1) - compiler->addInstruction(vm::OP_NULL); - else - compiler->addInstruction(vm::OP_READ_ARG, { static_cast(index) }); - } else - compiler->addInstruction(vm::OP_NULL); + const std::string &argName = compiler->field("VALUE")->value().toString(); + return compiler->addProcedureArgument(argName); } diff --git a/src/blocks/customblocks.h b/src/blocks/customblocks.h index a40e37c3..55572ba9 100644 --- a/src/blocks/customblocks.h +++ b/src/blocks/customblocks.h @@ -7,29 +7,17 @@ namespace libscratchcpp { -class Compiler; - class CustomBlocks : public IExtension { public: - enum Inputs - { - CUSTOM_BLOCK - }; - - enum Fields - { - VALUE - }; - std::string name() const override; std::string description() const override; void registerBlocks(IEngine *engine) override; - static void compileDefinition(Compiler *compiler); - static void compileCall(Compiler *compiler); - static void compileArgument(Compiler *compiler); + private: + static CompilerValue *compileCall(Compiler *compiler); + static CompilerValue *compileArgument(Compiler *compiler); }; } // namespace libscratchcpp diff --git a/src/blocks/eventblocks.cpp b/src/blocks/eventblocks.cpp index 757e2bda..41a3d75f 100644 --- a/src/blocks/eventblocks.cpp +++ b/src/blocks/eventblocks.cpp @@ -1,25 +1,19 @@ // SPDX-License-Identifier: Apache-2.0 #include -#include #include -#include -#include -#include -#include -#include -#include #include -#include +#include +#include +#include +#include +#include +#include #include "eventblocks.h" -#include "audio/audioinput.h" -#include "audio/iaudioloudness.h" using namespace libscratchcpp; -IAudioInput *EventBlocks::audioInput = nullptr; - std::string EventBlocks::name() const { return "Events"; @@ -32,192 +26,109 @@ std::string libscratchcpp::EventBlocks::description() const void EventBlocks::registerBlocks(IEngine *engine) { - // Blocks engine->addCompileFunction(this, "event_whentouchingobject", &compileWhenTouchingObject); engine->addCompileFunction(this, "event_whenflagclicked", &compileWhenFlagClicked); engine->addCompileFunction(this, "event_whenthisspriteclicked", &compileWhenThisSpriteClicked); engine->addCompileFunction(this, "event_whenstageclicked", &compileWhenStageClicked); - engine->addCompileFunction(this, "event_broadcast", &compileBroadcast); - engine->addCompileFunction(this, "event_broadcastandwait", &compileBroadcastAndWait); engine->addCompileFunction(this, "event_whenbroadcastreceived", &compileWhenBroadcastReceived); engine->addCompileFunction(this, "event_whenbackdropswitchesto", &compileWhenBackdropSwitchesTo); engine->addCompileFunction(this, "event_whengreaterthan", &compileWhenGreaterThan); + engine->addCompileFunction(this, "event_broadcast", &compileBroadcast); + engine->addCompileFunction(this, "event_broadcastandwait", &compileBroadcastAndWait); engine->addCompileFunction(this, "event_whenkeypressed", &compileWhenKeyPressed); - - // Hat predicates - engine->addHatPredicateCompileFunction(this, "event_whentouchingobject", &compileWhenTouchingObjectPredicate); - engine->addHatPredicateCompileFunction(this, "event_whengreaterthan", &compileWhenGreaterThanPredicate); - - // Inputs - engine->addInput(this, "TOUCHINGOBJECTMENU", TOUCHINGOBJECTMENU); - engine->addInput(this, "BROADCAST_INPUT", BROADCAST_INPUT); - engine->addInput(this, "VALUE", VALUE); - - // Fields - engine->addField(this, "BROADCAST_OPTION", BROADCAST_OPTION); - engine->addField(this, "BACKDROP", BACKDROP); - engine->addField(this, "WHENGREATERTHANMENU", WHENGREATERTHANMENU); - engine->addField(this, "KEY_OPTION", KEY_OPTION); - - // Fields values - engine->addFieldValue(this, "LOUDNESS", Loudness); - engine->addFieldValue(this, "TIMER", Timer); } -void EventBlocks::compileWhenTouchingObjectPredicate(Compiler *compiler) -{ - Input *input = compiler->input(TOUCHINGOBJECTMENU); - - if (input->pointsToDropdownMenu()) { - std::string value = input->selectedMenuItem(); - - compiler->addConstValue(value); - compiler->addFunctionCall(&whenTouchingObjectPredicate); - } else { - compiler->addInput(input); - compiler->addFunctionCall(&whenTouchingObjectPredicate); - } -} - -void EventBlocks::compileWhenTouchingObject(Compiler *compiler) +CompilerValue *EventBlocks::compileWhenTouchingObject(Compiler *compiler) { compiler->engine()->addWhenTouchingObjectScript(compiler->block()); + return nullptr; } -void EventBlocks::compileWhenFlagClicked(Compiler *compiler) +CompilerValue *EventBlocks::compileWhenFlagClicked(Compiler *compiler) { compiler->engine()->addGreenFlagScript(compiler->block()); + return nullptr; } -void EventBlocks::compileWhenThisSpriteClicked(Compiler *compiler) +CompilerValue *EventBlocks::compileWhenThisSpriteClicked(Compiler *compiler) { compiler->engine()->addTargetClickScript(compiler->block()); + return nullptr; } -void EventBlocks::compileWhenStageClicked(Compiler *compiler) +CompilerValue *EventBlocks::compileWhenStageClicked(Compiler *compiler) { compiler->engine()->addTargetClickScript(compiler->block()); + return nullptr; } -void EventBlocks::compileBroadcast(Compiler *compiler) +CompilerValue *EventBlocks::compileWhenBroadcastReceived(Compiler *compiler) { - compiler->addInput(BROADCAST_INPUT); - compiler->addFunctionCall(&broadcast); -} + auto block = compiler->block(); + Field *field = compiler->field("BROADCAST_OPTION"); -void EventBlocks::compileBroadcastAndWait(Compiler *compiler) -{ - compiler->addInput(BROADCAST_INPUT); - compiler->addFunctionCall(&broadcastAndWait); -} - -void EventBlocks::compileWhenBroadcastReceived(Compiler *compiler) -{ - Field *field = compiler->field(BROADCAST_OPTION); - auto broadcast = std::static_pointer_cast(field->valuePtr()); + if (field) { + auto broadcast = std::static_pointer_cast(field->valuePtr()); + compiler->engine()->addBroadcastScript(block, field, broadcast.get()); + } - compiler->engine()->addBroadcastScript(compiler->block(), field, broadcast.get()); + return nullptr; } -void EventBlocks::compileWhenBackdropSwitchesTo(Compiler *compiler) +CompilerValue *EventBlocks::compileWhenBackdropSwitchesTo(Compiler *compiler) { - compiler->engine()->addBackdropChangeScript(compiler->block(), compiler->field(BACKDROP)); -} + auto block = compiler->block(); + Field *field = compiler->field("BACKDROP"); -void EventBlocks::compileWhenGreaterThanPredicate(Compiler *compiler) -{ - Field *field = compiler->field(WHENGREATERTHANMENU); - BlockFunc predicate = nullptr; + if (field) + compiler->engine()->addBackdropChangeScript(block, field); - if (field) { - switch (field->specialValueId()) { - case Loudness: - predicate = &whenLoudnessGreaterThanPredicate; - break; - - case Timer: - predicate = &whenTimerGreaterThanPredicate; - break; - - default: - compiler->addInstruction(vm::OP_NULL); - return; - } - } - - if (predicate) { - compiler->addInput(VALUE); - compiler->addFunctionCall(predicate); - } + return nullptr; } -void EventBlocks::compileWhenGreaterThan(Compiler *compiler) +CompilerValue *EventBlocks::compileWhenGreaterThan(Compiler *compiler) { compiler->engine()->addWhenGreaterThanScript(compiler->block()); + return nullptr; } -void EventBlocks::compileWhenKeyPressed(Compiler *compiler) +CompilerValue *EventBlocks::compileBroadcast(Compiler *compiler) { - // NOTE: Field values don't have to be registered because keys are referenced by their names - compiler->engine()->addKeyPressScript(compiler->block(), compiler->field(KEY_OPTION)); + auto input = compiler->addInput("BROADCAST_INPUT"); + auto wait = compiler->addConstValue(false); + compiler->addFunctionCallWithCtx("event_broadcast", Compiler::StaticType::Void, { Compiler::StaticType::String, Compiler::StaticType::Bool }, { input, wait }); + return nullptr; } -unsigned int EventBlocks::whenTouchingObjectPredicate(VirtualMachine *vm) +CompilerValue *EventBlocks::compileBroadcastAndWait(Compiler *compiler) { - std::string value = vm->getInput(0, 1)->toString(); - - if (value == "_mouse_") - vm->replaceReturnValue(vm->target()->touchingPoint(vm->engine()->mouseX(), vm->engine()->mouseY()), 1); - else if (value == "_edge_") - vm->replaceReturnValue(vm->target()->touchingEdge(), 1); - else { - Target *target = vm->engine()->targetAt(vm->engine()->findTarget(value)); - - if (target && !target->isStage()) - vm->replaceReturnValue(vm->target()->touchingSprite(static_cast(target)), 1); - else - vm->replaceReturnValue(false, 1); - } - - return 0; + auto input = compiler->addInput("BROADCAST_INPUT"); + auto wait = compiler->addConstValue(true); + compiler->addFunctionCallWithCtx("event_broadcast", Compiler::StaticType::Void, { Compiler::StaticType::String, Compiler::StaticType::Bool }, { input, wait }); + compiler->createYield(); + return nullptr; } -unsigned int EventBlocks::broadcast(VirtualMachine *vm) +CompilerValue *EventBlocks::compileWhenKeyPressed(Compiler *compiler) { - std::vector broadcasts = vm->engine()->findBroadcasts(vm->getInput(0, 1)->toString()); + auto block = compiler->block(); + Field *field = compiler->field("KEY_OPTION"); - for (int index : broadcasts) - vm->engine()->broadcast(index, vm->thread(), false); + if (field) + compiler->engine()->addKeyPressScript(block, field); - return 1; + return nullptr; } -unsigned int EventBlocks::broadcastAndWait(VirtualMachine *vm) +extern "C" void event_broadcast(ExecutionContext *ctx, const char *name, bool wait) { - std::vector broadcasts = vm->engine()->findBroadcasts(vm->getInput(0, 1)->toString()); + Thread *thread = ctx->thread(); + IEngine *engine = thread->engine(); + std::vector broadcasts = engine->findBroadcasts(name); for (int index : broadcasts) - vm->engine()->broadcast(index, vm->thread(), true); - - vm->promise(); - - return 1; -} + engine->broadcast(index, thread, wait); -unsigned int EventBlocks::whenLoudnessGreaterThanPredicate(VirtualMachine *vm) -{ - if (!audioInput) - audioInput = AudioInput::instance().get(); - - auto audioLoudness = audioInput->audioLoudness(); - const Value &operand = *vm->getInput(0, 1); - vm->replaceReturnValue(Value(audioLoudness->getLoudness()) > operand, 1); - return 0; -} - -unsigned int EventBlocks::whenTimerGreaterThanPredicate(VirtualMachine *vm) -{ - const Value &operand = *vm->getInput(0, 1); - vm->replaceReturnValue(Value(vm->engine()->timer()->value()) > operand, 1); - return 0; + if (wait) + ctx->setPromise(std::make_shared()); } diff --git a/src/blocks/eventblocks.h b/src/blocks/eventblocks.h index e4d28f4a..34454ebc 100644 --- a/src/blocks/eventblocks.h +++ b/src/blocks/eventblocks.h @@ -7,62 +7,25 @@ namespace libscratchcpp { -class Compiler; -class VirtualMachine; -class IAudioInput; - -/*! \brief The EventBlocks class contains the implementation of event blocks. */ class EventBlocks : public IExtension { public: - enum Inputs - { - TOUCHINGOBJECTMENU, - BROADCAST_INPUT, - VALUE - }; - - enum Fields - { - BROADCAST_OPTION, - BACKDROP, - WHENGREATERTHANMENU, - KEY_OPTION - }; - - enum FieldValues - { - Loudness, - Timer - }; - std::string name() const override; std::string description() const override; void registerBlocks(IEngine *engine) override; - static void compileWhenTouchingObjectPredicate(Compiler *compiler); - static void compileWhenTouchingObject(Compiler *compiler); - static void compileWhenFlagClicked(Compiler *compiler); - static void compileWhenThisSpriteClicked(Compiler *compiler); - static void compileWhenStageClicked(Compiler *compiler); - static void compileBroadcast(Compiler *compiler); - static void compileBroadcastAndWait(Compiler *compiler); - static void compileWhenBroadcastReceived(Compiler *compiler); - static void compileWhenBackdropSwitchesTo(Compiler *compiler); - static void compileWhenGreaterThanPredicate(Compiler *compiler); - static void compileWhenGreaterThan(Compiler *compiler); - static void compileWhenKeyPressed(Compiler *compiler); - - static unsigned int whenTouchingObjectPredicate(VirtualMachine *vm); - - static unsigned int broadcast(VirtualMachine *vm); - static unsigned int broadcastAndWait(VirtualMachine *vm); - - static unsigned int whenLoudnessGreaterThanPredicate(VirtualMachine *vm); - static unsigned int whenTimerGreaterThanPredicate(VirtualMachine *vm); - - static IAudioInput *audioInput; + private: + static CompilerValue *compileWhenTouchingObject(Compiler *compiler); + static CompilerValue *compileWhenFlagClicked(Compiler *compiler); + static CompilerValue *compileWhenThisSpriteClicked(Compiler *compiler); + static CompilerValue *compileWhenStageClicked(Compiler *compiler); + static CompilerValue *compileWhenBroadcastReceived(Compiler *compiler); + static CompilerValue *compileWhenBackdropSwitchesTo(Compiler *compiler); + static CompilerValue *compileWhenGreaterThan(Compiler *compiler); + static CompilerValue *compileBroadcast(Compiler *compiler); + static CompilerValue *compileBroadcastAndWait(Compiler *compiler); + static CompilerValue *compileWhenKeyPressed(Compiler *compiler); }; } // namespace libscratchcpp diff --git a/src/blocks/listblocks.cpp b/src/blocks/listblocks.cpp index 135dd0bd..ee56014b 100644 --- a/src/blocks/listblocks.cpp +++ b/src/blocks/listblocks.cpp @@ -2,12 +2,9 @@ #include #include +#include #include -#include #include -#include -#include -#include #include "listblocks.h" @@ -25,195 +22,170 @@ std::string ListBlocks::description() const void ListBlocks::registerBlocks(IEngine *engine) { - // Blocks - engine->addCompileFunction(this, "data_listcontents", &compileListContents); engine->addCompileFunction(this, "data_addtolist", &compileAddToList); - engine->addCompileFunction(this, "data_deleteoflist", &compileDeleteFromList); + engine->addCompileFunction(this, "data_deleteoflist", &compileDeleteOfList); engine->addCompileFunction(this, "data_deletealloflist", &compileDeleteAllOfList); - engine->addCompileFunction(this, "data_insertatlist", &compileInsertToList); + engine->addCompileFunction(this, "data_insertatlist", &compileInsertAtList); engine->addCompileFunction(this, "data_replaceitemoflist", &compileReplaceItemOfList); engine->addCompileFunction(this, "data_itemoflist", &compileItemOfList); - engine->addCompileFunction(this, "data_itemnumoflist", &compileItemNumberInList); + engine->addCompileFunction(this, "data_itemnumoflist", &compileItemNumOfList); engine->addCompileFunction(this, "data_lengthoflist", &compileLengthOfList); engine->addCompileFunction(this, "data_listcontainsitem", &compileListContainsItem); - engine->addCompileFunction(this, "data_showlist", &compileShowList); - engine->addCompileFunction(this, "data_hidelist", &compileHideList); - - // Monitor names - engine->addMonitorNameFunction(this, "data_listcontents", &listContentsMonitorName); - - // Inputs - engine->addInput(this, "ITEM", ITEM); - engine->addInput(this, "INDEX", INDEX); - - // Fields - engine->addField(this, "LIST", LIST); } -void ListBlocks::compileListContents(Compiler *compiler) +CompilerValue *ListBlocks::compileAddToList(Compiler *compiler) { - // NOTE: This block is only used by list monitors - // Instead of returning the actual list contents, let's just return the index of the list - // and let the renderer read the list using the index - compiler->addConstValue(static_cast(compiler->listIndex(compiler->field(LIST)->valuePtr()))); -} + auto list = compiler->field("LIST")->valuePtr(); + assert(list); -void ListBlocks::compileAddToList(Compiler *compiler) -{ - compiler->addInput(ITEM); - compiler->addInstruction(vm::OP_LIST_APPEND, { compiler->listIndex(compiler->field(LIST)->valuePtr()) }); -} + if (list) + compiler->createListAppend(static_cast(list.get()), compiler->addInput("ITEM")); -void ListBlocks::compileDeleteFromList(Compiler *compiler) -{ - compiler->addInput(INDEX); - compiler->addInstruction(vm::OP_LIST_DEL, { compiler->listIndex(compiler->field(LIST)->valuePtr()) }); + return nullptr; } -void ListBlocks::compileDeleteAllOfList(Compiler *compiler) +CompilerValue *ListBlocks::getListIndex(Compiler *compiler, CompilerValue *input, List *list, CompilerValue *listSize) { - compiler->addInstruction(vm::OP_LIST_DEL_ALL, { compiler->listIndex(compiler->field(LIST)->valuePtr()) }); -} + CompilerLocalVariable *ret = compiler->createLocalVariable(Compiler::StaticType::Number); -void ListBlocks::compileInsertToList(Compiler *compiler) -{ - compiler->addInput(ITEM); - compiler->addInput(INDEX); - compiler->addInstruction(vm::OP_LIST_INSERT, { compiler->listIndex(compiler->field(LIST)->valuePtr()) }); -} + CompilerValue *isRandom1 = compiler->createStrCmpEQ(input, compiler->addConstValue("random"), true); + CompilerValue *isRandom2 = compiler->createStrCmpEQ(input, compiler->addConstValue("any"), true); + CompilerValue *isRandom = compiler->createOr(isRandom1, isRandom2); -void ListBlocks::compileReplaceItemOfList(Compiler *compiler) -{ - compiler->addInput(INDEX); - compiler->addInput(ITEM); - compiler->addInstruction(vm::OP_LIST_REPLACE, { compiler->listIndex(compiler->field(LIST)->valuePtr()) }); -} + compiler->beginIfStatement(isRandom); + { + CompilerValue *random = compiler->createRandomInt(compiler->addConstValue(1), listSize); + compiler->createLocalVariableWrite(ret, random); + } + compiler->beginElseBranch(); + { + CompilerValue *isLast = compiler->createStrCmpEQ(input, compiler->addConstValue("last"), true); + compiler->createLocalVariableWrite(ret, compiler->createSelect(isLast, listSize, input, Compiler::StaticType::Number)); + } + compiler->endIf(); -void ListBlocks::compileItemOfList(Compiler *compiler) -{ - compiler->addInput(INDEX); - compiler->addInstruction(vm::OP_LIST_GET_ITEM, { compiler->listIndex(compiler->field(LIST)->valuePtr()) }); + return compiler->addLocalVariableValue(ret); } -void ListBlocks::compileItemNumberInList(Compiler *compiler) +CompilerValue *ListBlocks::compileDeleteOfList(Compiler *compiler) { - compiler->addInput(ITEM); - compiler->addInstruction(vm::OP_LIST_INDEX_OF, { compiler->listIndex(compiler->field(LIST)->valuePtr()) }); -} + List *list = static_cast(compiler->field("LIST")->valuePtr().get()); + assert(list); -void ListBlocks::compileLengthOfList(Compiler *compiler) -{ - compiler->addInstruction(vm::OP_LIST_LENGTH, { compiler->listIndex(compiler->field(LIST)->valuePtr()) }); -} + if (list) { + CompilerValue *index = compiler->addInput("INDEX"); + CompilerValue *cond = compiler->createStrCmpEQ(index, compiler->addConstValue("all"), true); + compiler->beginIfStatement(cond); + { + compiler->createListClear(list); + } + compiler->beginElseBranch(); + { + CompilerValue *max = compiler->addListSize(list); + index = getListIndex(compiler, index, list, max); + index = compiler->createSub(index, compiler->addConstValue(1)); + compiler->createListRemove(list, index); + } + compiler->endIf(); + } -void ListBlocks::compileListContainsItem(Compiler *compiler) -{ - compiler->addInput(ITEM); - compiler->addInstruction(vm::OP_LIST_CONTAINS, { compiler->listIndex(compiler->field(LIST)->valuePtr()) }); + return nullptr; } -void ListBlocks::compileShowList(Compiler *compiler) +CompilerValue *ListBlocks::compileDeleteAllOfList(Compiler *compiler) { - Field *field = compiler->field(LIST); - assert(field); - List *var = static_cast(field->valuePtr().get()); - assert(var); + auto list = compiler->field("LIST")->valuePtr(); + assert(list); - compiler->addConstValue(var->id()); + if (list) + compiler->createListClear(static_cast(list.get())); - if (var->target() == static_cast(compiler->engine()->stage())) - compiler->addFunctionCall(&showGlobalList); - else - compiler->addFunctionCall(&showList); + return nullptr; } -void ListBlocks::compileHideList(Compiler *compiler) +CompilerValue *ListBlocks::compileInsertAtList(Compiler *compiler) { - Field *field = compiler->field(LIST); - assert(field); - List *var = static_cast(field->valuePtr().get()); - assert(var); - - compiler->addConstValue(var->id()); + List *list = static_cast(compiler->field("LIST")->valuePtr().get()); + assert(list); - if (var->target() == static_cast(compiler->engine()->stage())) - compiler->addFunctionCall(&hideGlobalList); - else - compiler->addFunctionCall(&hideList); -} - -void ListBlocks::setListVisible(std::shared_ptr list, bool visible, IEngine *engine) -{ if (list) { - Monitor *monitor = list->monitor(); - - if (!monitor) - monitor = engine->createListMonitor(list, "data_listcontents", "LIST", LIST, &compileListContents); - - monitor->setVisible(visible); + CompilerValue *index = compiler->addInput("INDEX"); + CompilerValue *max = compiler->createAdd(compiler->addListSize(list), compiler->addConstValue(1)); + index = getListIndex(compiler, index, list, max); + index = compiler->createSub(index, compiler->addConstValue(1)); + CompilerValue *item = compiler->addInput("ITEM"); + compiler->createListInsert(list, index, item); } + + return nullptr; } -unsigned int ListBlocks::showGlobalList(VirtualMachine *vm) +CompilerValue *ListBlocks::compileReplaceItemOfList(Compiler *compiler) { - if (Stage *target = vm->engine()->stage()) { - int index = target->findListById(vm->getInput(0, 1)->toString()); - setListVisible(target->listAt(index), true, vm->engine()); + List *list = static_cast(compiler->field("LIST")->valuePtr().get()); + assert(list); + + if (list) { + CompilerValue *index = compiler->addInput("INDEX"); + CompilerValue *max = compiler->addListSize(list); + index = getListIndex(compiler, index, list, max); + index = compiler->createSub(index, compiler->addConstValue(1)); + CompilerValue *item = compiler->addInput("ITEM"); + compiler->createListReplace(list, index, item); } - return 1; + return nullptr; } -unsigned int ListBlocks::showList(VirtualMachine *vm) +CompilerValue *ListBlocks::compileItemOfList(Compiler *compiler) { - if (Target *target = vm->target()) { - if (!target->isStage() && static_cast(target)->isClone()) { - Sprite *sprite = static_cast(target)->cloneSprite(); // use clone root list - int index = sprite->findListById(vm->getInput(0, 1)->toString()); - setListVisible(sprite->listAt(index), true, vm->engine()); - } else { - int index = target->findListById(vm->getInput(0, 1)->toString()); - setListVisible(target->listAt(index), true, vm->engine()); - } + List *list = static_cast(compiler->field("LIST")->valuePtr().get()); + assert(list); + + if (list) { + CompilerValue *index = compiler->addInput("INDEX"); + CompilerValue *max = compiler->addListSize(list); + index = getListIndex(compiler, index, list, max); + index = compiler->createSub(index, compiler->addConstValue(1)); + return compiler->addListItem(list, index); } - return 1; + return nullptr; } -unsigned int ListBlocks::hideGlobalList(VirtualMachine *vm) +CompilerValue *ListBlocks::compileItemNumOfList(Compiler *compiler) { - if (Stage *target = vm->engine()->stage()) { - int index = target->findListById(vm->getInput(0, 1)->toString()); - setListVisible(target->listAt(index), false, vm->engine()); + List *list = static_cast(compiler->field("LIST")->valuePtr().get()); + assert(list); + + if (list) { + CompilerValue *item = compiler->addInput("ITEM"); + return compiler->createAdd(compiler->addListItemIndex(list, item), compiler->addConstValue(1)); } - return 1; + return nullptr; } -unsigned int ListBlocks::hideList(VirtualMachine *vm) +CompilerValue *ListBlocks::compileLengthOfList(Compiler *compiler) { - if (Target *target = vm->target()) { - if (!target->isStage() && static_cast(target)->isClone()) { - Sprite *sprite = static_cast(target)->cloneSprite(); // use clone root list - int index = sprite->findListById(vm->getInput(0, 1)->toString()); - setListVisible(sprite->listAt(index), false, vm->engine()); - } else { - int index = target->findListById(vm->getInput(0, 1)->toString()); - setListVisible(target->listAt(index), false, vm->engine()); - } - } + List *list = static_cast(compiler->field("LIST")->valuePtr().get()); + assert(list); - return 1; + if (list) + return compiler->addListSize(list); + + return nullptr; } -const std::string &ListBlocks::listContentsMonitorName(Block *block) +CompilerValue *ListBlocks::compileListContainsItem(Compiler *compiler) { - List *list = dynamic_cast(block->findFieldById(LIST)->valuePtr().get()); + List *list = static_cast(compiler->field("LIST")->valuePtr().get()); + assert(list); - if (list) - return list->name(); - else { - static const std::string empty = ""; - return empty; + if (list) { + CompilerValue *item = compiler->addInput("ITEM"); + return compiler->addListContains(list, item); } + + return nullptr; } diff --git a/src/blocks/listblocks.h b/src/blocks/listblocks.h index 69e49aa3..3a8d0126 100644 --- a/src/blocks/listblocks.h +++ b/src/blocks/listblocks.h @@ -3,58 +3,31 @@ #pragma once #include -#include namespace libscratchcpp { -class Compiler; class List; -/*! \brief The ListBlocks class contains the implementation of list blocks. */ class ListBlocks : public IExtension { public: - enum Inputs - { - ITEM, - INDEX - }; - - enum Fields - { - LIST - }; - std::string name() const override; std::string description() const override; void registerBlocks(IEngine *engine) override; - static void compileListContents(Compiler *compiler); - static void compileAddToList(Compiler *compiler); - static void compileDeleteFromList(Compiler *compiler); - static void compileDeleteAllOfList(Compiler *compiler); - static void compileInsertToList(Compiler *compiler); - static void compileReplaceItemOfList(Compiler *compiler); - static void compileItemOfList(Compiler *compiler); - static void compileItemNumberInList(Compiler *compiler); - static void compileLengthOfList(Compiler *compiler); - static void compileListContainsItem(Compiler *compiler); - static void compileShowList(Compiler *compiler); - static void compileHideList(Compiler *compiler); - - static void setListVisible(std::shared_ptr list, bool visible, IEngine *engine); - - static unsigned int showGlobalList(VirtualMachine *vm); - static unsigned int showList(VirtualMachine *vm); - static unsigned int hideGlobalList(VirtualMachine *vm); - static unsigned int hideList(VirtualMachine *vm); - - static const std::string &listContentsMonitorName(Block *block); - private: - static int validateIndex(size_t index, size_t listLength); + static CompilerValue *compileAddToList(Compiler *compiler); + static CompilerValue *getListIndex(Compiler *compiler, CompilerValue *input, List *list, CompilerValue *listSize); + static CompilerValue *compileDeleteOfList(Compiler *compiler); + static CompilerValue *compileDeleteAllOfList(Compiler *compiler); + static CompilerValue *compileInsertAtList(Compiler *compiler); + static CompilerValue *compileReplaceItemOfList(Compiler *compiler); + static CompilerValue *compileItemOfList(Compiler *compiler); + static CompilerValue *compileItemNumOfList(Compiler *compiler); + static CompilerValue *compileLengthOfList(Compiler *compiler); + static CompilerValue *compileListContainsItem(Compiler *compiler); }; } // namespace libscratchcpp diff --git a/src/blocks/looksblocks.cpp b/src/blocks/looksblocks.cpp index f142f34c..0d471fb0 100644 --- a/src/blocks/looksblocks.cpp +++ b/src/blocks/looksblocks.cpp @@ -1,37 +1,9 @@ // SPDX-License-Identifier: Apache-2.0 -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - #include "looksblocks.h" -#include "../engine/internal/randomgenerator.h" -#include "../engine/internal/clock.h" using namespace libscratchcpp; -IRandomGenerator *LooksBlocks::rng = nullptr; -IClock *LooksBlocks::clock = nullptr; - -// TODO: Use C++20 -template -void erase_if(ContainerT &items, const PredicateT &predicate) -{ - for (auto it = items.begin(); it != items.end();) { - if (predicate(*it)) - it = items.erase(it); - else - ++it; - } -} - std::string LooksBlocks::name() const { return "Looks"; @@ -44,1059 +16,4 @@ std::string LooksBlocks::description() const void LooksBlocks::registerBlocks(IEngine *engine) { - // Blocks - engine->addCompileFunction(this, "looks_sayforsecs", &compileSayForSecs); - engine->addCompileFunction(this, "looks_say", &compileSay); - engine->addCompileFunction(this, "looks_thinkforsecs", &compileThinkForSecs); - engine->addCompileFunction(this, "looks_think", &compileThink); - engine->addCompileFunction(this, "looks_show", &compileShow); - engine->addCompileFunction(this, "looks_hide", &compileHide); - engine->addCompileFunction(this, "looks_changeeffectby", &compileChangeEffectBy); - engine->addCompileFunction(this, "looks_seteffectto", &compileSetEffectTo); - engine->addCompileFunction(this, "looks_cleargraphiceffects", &compileClearGraphicEffects); - engine->addCompileFunction(this, "looks_changesizeby", &compileChangeSizeBy); - engine->addCompileFunction(this, "looks_setsizeto", &compileSetSizeTo); - engine->addCompileFunction(this, "looks_size", &compileSize); - engine->addCompileFunction(this, "looks_switchcostumeto", &compileSwitchCostumeTo); - engine->addCompileFunction(this, "looks_nextcostume", &compileNextCostume); - engine->addCompileFunction(this, "looks_switchbackdropto", &compileSwitchBackdropTo); - engine->addCompileFunction(this, "looks_switchbackdroptoandwait", &compileSwitchBackdropToAndWait); - engine->addCompileFunction(this, "looks_nextbackdrop", &compileNextBackdrop); - engine->addCompileFunction(this, "looks_gotofrontback", &compileGoToFrontBack); - engine->addCompileFunction(this, "looks_goforwardbackwardlayers", &compileGoForwardBackwardLayers); - engine->addCompileFunction(this, "looks_costumenumbername", &compileCostumeNumberName); - engine->addCompileFunction(this, "looks_backdropnumbername", &compileBackdropNumberName); - - // Monitor names - engine->addMonitorNameFunction(this, "looks_costumenumbername", &costumeNumberNameMonitorName); - engine->addMonitorNameFunction(this, "looks_backdropnumbername", &backdropNumberNameMonitorName); - engine->addMonitorNameFunction(this, "looks_size", &sizeMonitorName); - - // Inputs - engine->addInput(this, "MESSAGE", MESSAGE); - engine->addInput(this, "SECS", SECS); - engine->addInput(this, "CHANGE", CHANGE); - engine->addInput(this, "SIZE", SIZE); - engine->addInput(this, "COSTUME", COSTUME); - engine->addInput(this, "BACKDROP", BACKDROP); - engine->addInput(this, "VALUE", VALUE); - engine->addInput(this, "NUM", NUM); - - // Fields - engine->addField(this, "NUMBER_NAME", NUMBER_NAME); - engine->addField(this, "EFFECT", EFFECT); - engine->addField(this, "FRONT_BACK", FRONT_BACK); - engine->addField(this, "FORWARD_BACKWARD", FORWARD_BACKWARD); - - // Field values - engine->addFieldValue(this, "number", Number); - engine->addFieldValue(this, "name", Name); - engine->addFieldValue(this, "COLOR", ColorEffect); - engine->addFieldValue(this, "FISHEYE", FisheyeEffect); - engine->addFieldValue(this, "WHIRL", WhirlEffect); - engine->addFieldValue(this, "PIXELATE", PixelateEffect); - engine->addFieldValue(this, "MOSAIC", MosaicEffect); - engine->addFieldValue(this, "BRIGHTNESS", BrightnessEffect); - engine->addFieldValue(this, "GHOST", GhostEffect); - engine->addFieldValue(this, "front", Front); - engine->addFieldValue(this, "back", Back); - engine->addFieldValue(this, "forward", Forward); - engine->addFieldValue(this, "backward", Backward); -} - -void LooksBlocks::onInit(IEngine *engine) -{ - engine->threadAboutToStop().connect([](Thread *thread) { - m_timeMap.erase(thread->vm()); - erase_if(m_waitingBubbles, [thread](const std::pair &pair) { return pair.second == thread->vm(); }); - }); - - engine->stopped().connect([engine]() { - const auto &targets = engine->targets(); - - for (auto target : targets) { - target->bubble()->setText(""); - target->clearGraphicsEffects(); - } - }); -} - -void LooksBlocks::compileSayForSecs(Compiler *compiler) -{ - compiler->addInput(MESSAGE); - compiler->addInput(SECS); - compiler->addFunctionCall(&startSayForSecs); - compiler->addFunctionCall(&sayForSecs); -} - -void LooksBlocks::compileSay(Compiler *compiler) -{ - compiler->addInput(MESSAGE); - compiler->addFunctionCall(&say); -} - -void LooksBlocks::compileThinkForSecs(Compiler *compiler) -{ - compiler->addInput(MESSAGE); - compiler->addInput(SECS); - compiler->addFunctionCall(&startThinkForSecs); - compiler->addFunctionCall(&thinkForSecs); -} - -void LooksBlocks::compileThink(Compiler *compiler) -{ - compiler->addInput(MESSAGE); - compiler->addFunctionCall(&think); -} - -void LooksBlocks::compileShow(Compiler *compiler) -{ - compiler->addFunctionCall(&show); -} - -void LooksBlocks::compileHide(Compiler *compiler) -{ - compiler->addFunctionCall(&hide); -} - -void LooksBlocks::compileChangeEffectBy(Compiler *compiler) -{ - int option = compiler->field(EFFECT)->specialValueId(); - - switch (option) { - case ColorEffect: - if (!m_colorEffect) - m_colorEffect = ScratchConfiguration::getGraphicsEffect("color"); - - compiler->addInput(CHANGE); - compiler->addFunctionCall(&changeColorEffectBy); - break; - - case FisheyeEffect: - if (!m_fisheyeEffect) - m_fisheyeEffect = ScratchConfiguration::getGraphicsEffect("fisheye"); - - compiler->addInput(CHANGE); - compiler->addFunctionCall(&changeFisheyeEffectBy); - break; - - case WhirlEffect: - if (!m_whirlEffect) - m_whirlEffect = ScratchConfiguration::getGraphicsEffect("whirl"); - - compiler->addInput(CHANGE); - compiler->addFunctionCall(&changeWhirlEffectBy); - break; - - case PixelateEffect: - if (!m_pixelateEffect) - m_pixelateEffect = ScratchConfiguration::getGraphicsEffect("pixelate"); - - compiler->addInput(CHANGE); - compiler->addFunctionCall(&changePixelateEffectBy); - break; - - case MosaicEffect: - if (!m_mosaicEffect) - m_mosaicEffect = ScratchConfiguration::getGraphicsEffect("mosaic"); - - compiler->addInput(CHANGE); - compiler->addFunctionCall(&changeMosaicEffectBy); - break; - - case BrightnessEffect: - if (!m_brightnessEffect) - m_brightnessEffect = ScratchConfiguration::getGraphicsEffect("brightness"); - - compiler->addInput(CHANGE); - compiler->addFunctionCall(&changeBrightnessEffectBy); - break; - - case GhostEffect: - if (!m_ghostEffect) - m_ghostEffect = ScratchConfiguration::getGraphicsEffect("ghost"); - - compiler->addInput(CHANGE); - compiler->addFunctionCall(&changeGhostEffectBy); - break; - - default: - IGraphicsEffect *effect = ScratchConfiguration::getGraphicsEffect(compiler->field(EFFECT)->value().toString()); - - if (effect) { - auto it = std::find(m_customGraphicsEffects.begin(), m_customGraphicsEffects.end(), effect); - size_t index; - - if (it == m_customGraphicsEffects.end()) { - index = m_customGraphicsEffects.size(); - m_customGraphicsEffects.push_back(effect); - } else - index = it - m_customGraphicsEffects.begin(); - - compiler->addConstValue(index); - compiler->addInput(CHANGE); - compiler->addFunctionCall(&changeEffectBy); - } - - break; - } -} - -void LooksBlocks::compileSetEffectTo(Compiler *compiler) -{ - int option = compiler->field(EFFECT)->specialValueId(); - - switch (option) { - case ColorEffect: - if (!m_colorEffect) - m_colorEffect = ScratchConfiguration::getGraphicsEffect("color"); - - compiler->addInput(VALUE); - compiler->addFunctionCall(&setColorEffectTo); - break; - - case FisheyeEffect: - if (!m_fisheyeEffect) - m_fisheyeEffect = ScratchConfiguration::getGraphicsEffect("fisheye"); - - compiler->addInput(VALUE); - compiler->addFunctionCall(&setFisheyeEffectTo); - break; - - case WhirlEffect: - if (!m_whirlEffect) - m_whirlEffect = ScratchConfiguration::getGraphicsEffect("whirl"); - - compiler->addInput(VALUE); - compiler->addFunctionCall(&setWhirlEffectTo); - break; - - case PixelateEffect: - if (!m_pixelateEffect) - m_pixelateEffect = ScratchConfiguration::getGraphicsEffect("pixelate"); - - compiler->addInput(VALUE); - compiler->addFunctionCall(&setPixelateEffectTo); - break; - - case MosaicEffect: - if (!m_mosaicEffect) - m_mosaicEffect = ScratchConfiguration::getGraphicsEffect("mosaic"); - - compiler->addInput(VALUE); - compiler->addFunctionCall(&setMosaicEffectTo); - break; - - case BrightnessEffect: - if (!m_brightnessEffect) - m_brightnessEffect = ScratchConfiguration::getGraphicsEffect("brightness"); - - compiler->addInput(VALUE); - compiler->addFunctionCall(&setBrightnessEffectTo); - break; - - case GhostEffect: - if (!m_ghostEffect) - m_ghostEffect = ScratchConfiguration::getGraphicsEffect("ghost"); - - compiler->addInput(VALUE); - compiler->addFunctionCall(&setGhostEffectTo); - break; - - default: - IGraphicsEffect *effect = ScratchConfiguration::getGraphicsEffect(compiler->field(EFFECT)->value().toString()); - - if (effect) { - auto it = std::find(m_customGraphicsEffects.begin(), m_customGraphicsEffects.end(), effect); - size_t index; - - if (it == m_customGraphicsEffects.end()) { - index = m_customGraphicsEffects.size(); - m_customGraphicsEffects.push_back(effect); - } else - index = it - m_customGraphicsEffects.begin(); - - compiler->addConstValue(index); - compiler->addInput(VALUE); - compiler->addFunctionCall(&setEffectTo); - } - - break; - } -} - -void LooksBlocks::compileClearGraphicEffects(Compiler *compiler) -{ - compiler->addFunctionCall(&clearGraphicEffects); -} - -void LooksBlocks::compileChangeSizeBy(Compiler *compiler) -{ - compiler->addInput(CHANGE); - compiler->addFunctionCall(&changeSizeBy); -} - -void LooksBlocks::compileSetSizeTo(Compiler *compiler) -{ - compiler->addInput(SIZE); - compiler->addFunctionCall(&setSizeTo); -} - -void LooksBlocks::compileSize(Compiler *compiler) -{ - compiler->addFunctionCall(&size); -} - -void LooksBlocks::compileSwitchCostumeTo(Compiler *compiler) -{ - Target *target = compiler->target(); - - if (!target) - return; - - compiler->addInput(COSTUME); - compiler->addFunctionCall(&switchCostumeTo); -} - -void LooksBlocks::compileNextCostume(Compiler *compiler) -{ - compiler->addFunctionCall(&nextCostume); -} - -void LooksBlocks::compileSwitchBackdropTo(Compiler *compiler) -{ - compiler->addInput(BACKDROP); - compiler->addFunctionCall(&switchBackdropTo); -} - -void LooksBlocks::compileSwitchBackdropToAndWait(Compiler *compiler) -{ - compiler->addInput(BACKDROP); - compiler->addFunctionCall(&switchBackdropToAndWait); -} - -void LooksBlocks::compileNextBackdrop(Compiler *compiler) -{ - compiler->addFunctionCall(&nextBackdrop); -} - -void LooksBlocks::compileGoToFrontBack(Compiler *compiler) -{ - int option = compiler->field(FRONT_BACK)->specialValueId(); - - switch (option) { - case Front: - compiler->addFunctionCall(&goToFront); - break; - - case Back: - compiler->addFunctionCall(&goToBack); - break; - } -} - -void LooksBlocks::compileGoForwardBackwardLayers(Compiler *compiler) -{ - int option = compiler->field(FORWARD_BACKWARD)->specialValueId(); - - switch (option) { - case Forward: - compiler->addInput(NUM); - compiler->addFunctionCall(&goForwardLayers); - break; - - case Backward: - compiler->addInput(NUM); - compiler->addFunctionCall(&goBackwardLayers); - break; - } -} - -void LooksBlocks::compileCostumeNumberName(Compiler *compiler) -{ - int option = compiler->field(NUMBER_NAME)->specialValueId(); - - switch (option) { - case Number: - compiler->addFunctionCall(&costumeNumber); - break; - - case Name: - compiler->addFunctionCall(&costumeName); - break; - } -} - -void LooksBlocks::compileBackdropNumberName(Compiler *compiler) -{ - int option = compiler->field(NUMBER_NAME)->specialValueId(); - - switch (option) { - case Number: - compiler->addFunctionCall(&backdropNumber); - break; - - case Name: - compiler->addFunctionCall(&backdropName); - break; - } -} - -const std::string &LooksBlocks::costumeNumberNameMonitorName(Block *block) -{ - int option = block->findFieldById(NUMBER_NAME)->specialValueId(); - - switch (option) { - case Number: { - static const std::string name = "costume number"; - return name; - } - - case Name: { - static const std::string name = "costume name"; - return name; - } - - default: { - static const std::string name = ""; - return name; - } - } -} - -const std::string &LooksBlocks::backdropNumberNameMonitorName(Block *block) -{ - int option = block->findFieldById(NUMBER_NAME)->specialValueId(); - - switch (option) { - case Number: { - static const std::string name = "backdrop number"; - return name; - } - - case Name: { - static const std::string name = "backdrop name"; - return name; - } - - default: { - static const std::string name = ""; - return name; - } - } -} - -const std::string &LooksBlocks::sizeMonitorName(Block *block) -{ - static const std::string name = "size"; - return name; -} - -void LooksBlocks::startWait(VirtualMachine *vm, double secs) -{ - if (!clock) - clock = Clock::instance().get(); - - auto currentTime = clock->currentSteadyTime(); - m_timeMap[vm] = { currentTime, secs * 1000 }; - vm->engine()->requestRedraw(); -} - -bool LooksBlocks::wait(VirtualMachine *vm) -{ - if (!clock) - clock = Clock::instance().get(); - - auto currentTime = clock->currentSteadyTime(); - assert(m_timeMap.count(vm) == 1); - - if (std::chrono::duration_cast(currentTime - m_timeMap[vm].first).count() >= m_timeMap[vm].second) { - m_timeMap.erase(vm); - vm->stop(true, true, false); - return true; - } else { - vm->stop(true, true, true); - return false; - } -} - -void LooksBlocks::showBubble(VirtualMachine *vm, TextBubble::Type type, const std::string &text) -{ - Target *target = vm->target(); - - if (target) { - target->bubble()->setType(type); - target->bubble()->setText(text); - m_waitingBubbles.erase(target); - } -} - -void LooksBlocks::hideBubble(Target *target) -{ - if (!target) - return; - - target->bubble()->setText(""); - m_waitingBubbles.erase(target); -} - -unsigned int LooksBlocks::startSayForSecs(VirtualMachine *vm) -{ - Target *target = vm->target(); - - if (target) { - showBubble(vm, TextBubble::Type::Say, vm->getInput(0, 2)->toString()); - m_waitingBubbles[target] = vm; - startWait(vm, vm->getInput(1, 2)->toDouble()); - } - - return 2; -} - -unsigned int LooksBlocks::sayForSecs(VirtualMachine *vm) -{ - if (wait(vm)) { - Target *target = vm->target(); - - if (target) { - auto it = m_waitingBubbles.find(target); - - // Clear bubble if it hasn't been changed - if (it != m_waitingBubbles.cend() && it->second == vm) - hideBubble(vm->target()); - } - } - - return 0; -} - -unsigned int LooksBlocks::say(VirtualMachine *vm) -{ - showBubble(vm, TextBubble::Type::Say, vm->getInput(0, 1)->toString()); - return 1; -} - -unsigned int LooksBlocks::startThinkForSecs(VirtualMachine *vm) -{ - Target *target = vm->target(); - - if (target) { - showBubble(vm, TextBubble::Type::Think, vm->getInput(0, 2)->toString()); - m_waitingBubbles[target] = vm; - startWait(vm, vm->getInput(1, 2)->toDouble()); - } - - return 2; -} - -unsigned int LooksBlocks::thinkForSecs(VirtualMachine *vm) -{ - return sayForSecs(vm); // there isn't any difference -} - -unsigned int LooksBlocks::think(VirtualMachine *vm) -{ - showBubble(vm, TextBubble::Type::Think, vm->getInput(0, 1)->toString()); - return 1; -} - -unsigned int LooksBlocks::show(VirtualMachine *vm) -{ - Sprite *sprite = dynamic_cast(vm->target()); - - if (sprite) - sprite->setVisible(true); - - return 0; -} - -unsigned int LooksBlocks::hide(VirtualMachine *vm) -{ - Sprite *sprite = dynamic_cast(vm->target()); - - if (sprite) - sprite->setVisible(false); - - return 0; -} - -unsigned int LooksBlocks::changeEffectBy(VirtualMachine *vm) -{ - Target *target = vm->target(); - - if (target) { - IGraphicsEffect *effect = m_customGraphicsEffects[vm->getInput(0, 2)->toLong()]; - target->setGraphicsEffectValue(effect, target->graphicsEffectValue(effect) + vm->getInput(1, 2)->toDouble()); - } - - return 2; -} - -unsigned int LooksBlocks::changeColorEffectBy(VirtualMachine *vm) -{ - Target *target = vm->target(); - - if (target) - target->setGraphicsEffectValue(m_colorEffect, target->graphicsEffectValue(m_colorEffect) + vm->getInput(0, 1)->toDouble()); - - return 1; -} - -unsigned int LooksBlocks::changeFisheyeEffectBy(VirtualMachine *vm) -{ - Target *target = vm->target(); - - if (target) - target->setGraphicsEffectValue(m_fisheyeEffect, target->graphicsEffectValue(m_fisheyeEffect) + vm->getInput(0, 1)->toDouble()); - - return 1; -} - -unsigned int LooksBlocks::changeWhirlEffectBy(VirtualMachine *vm) -{ - Target *target = vm->target(); - - if (target) - target->setGraphicsEffectValue(m_whirlEffect, target->graphicsEffectValue(m_whirlEffect) + vm->getInput(0, 1)->toDouble()); - - return 1; -} - -unsigned int LooksBlocks::changePixelateEffectBy(VirtualMachine *vm) -{ - Target *target = vm->target(); - - if (target) - target->setGraphicsEffectValue(m_pixelateEffect, target->graphicsEffectValue(m_pixelateEffect) + vm->getInput(0, 1)->toDouble()); - - return 1; -} - -unsigned int LooksBlocks::changeMosaicEffectBy(VirtualMachine *vm) -{ - Target *target = vm->target(); - - if (target) - target->setGraphicsEffectValue(m_mosaicEffect, target->graphicsEffectValue(m_mosaicEffect) + vm->getInput(0, 1)->toDouble()); - - return 1; -} - -unsigned int LooksBlocks::changeBrightnessEffectBy(VirtualMachine *vm) -{ - Target *target = vm->target(); - - if (target) - target->setGraphicsEffectValue(m_brightnessEffect, target->graphicsEffectValue(m_brightnessEffect) + vm->getInput(0, 1)->toDouble()); - - return 1; -} - -unsigned int LooksBlocks::changeGhostEffectBy(VirtualMachine *vm) -{ - Target *target = vm->target(); - - if (target) - target->setGraphicsEffectValue(m_ghostEffect, target->graphicsEffectValue(m_ghostEffect) + vm->getInput(0, 1)->toDouble()); - - return 1; -} - -unsigned int LooksBlocks::setEffectTo(VirtualMachine *vm) -{ - Target *target = vm->target(); - - if (target) - target->setGraphicsEffectValue(m_customGraphicsEffects[vm->getInput(0, 2)->toLong()], vm->getInput(1, 2)->toDouble()); - - return 2; -} - -unsigned int LooksBlocks::setColorEffectTo(VirtualMachine *vm) -{ - Target *target = vm->target(); - - if (target) - target->setGraphicsEffectValue(m_colorEffect, vm->getInput(0, 1)->toDouble()); - - return 1; -} - -unsigned int LooksBlocks::setFisheyeEffectTo(VirtualMachine *vm) -{ - Target *target = vm->target(); - - if (target) - target->setGraphicsEffectValue(m_fisheyeEffect, vm->getInput(0, 1)->toDouble()); - - return 1; -} - -unsigned int LooksBlocks::setWhirlEffectTo(VirtualMachine *vm) -{ - Target *target = vm->target(); - - if (target) - target->setGraphicsEffectValue(m_whirlEffect, vm->getInput(0, 1)->toDouble()); - - return 1; -} - -unsigned int LooksBlocks::setPixelateEffectTo(VirtualMachine *vm) -{ - Target *target = vm->target(); - - if (target) - target->setGraphicsEffectValue(m_pixelateEffect, vm->getInput(0, 1)->toDouble()); - - return 1; -} - -unsigned int LooksBlocks::setMosaicEffectTo(VirtualMachine *vm) -{ - Target *target = vm->target(); - - if (target) - target->setGraphicsEffectValue(m_mosaicEffect, vm->getInput(0, 1)->toDouble()); - - return 1; -} - -unsigned int LooksBlocks::setBrightnessEffectTo(VirtualMachine *vm) -{ - Target *target = vm->target(); - - if (target) - target->setGraphicsEffectValue(m_brightnessEffect, vm->getInput(0, 1)->toDouble()); - - return 1; -} - -unsigned int LooksBlocks::setGhostEffectTo(VirtualMachine *vm) -{ - Target *target = vm->target(); - - if (target) - target->setGraphicsEffectValue(m_ghostEffect, vm->getInput(0, 1)->toDouble()); - - return 1; -} - -unsigned int LooksBlocks::clearGraphicEffects(VirtualMachine *vm) -{ - Target *target = vm->target(); - - if (target) - target->clearGraphicsEffects(); - - return 0; -} - -unsigned int LooksBlocks::changeSizeBy(VirtualMachine *vm) -{ - Sprite *sprite = dynamic_cast(vm->target()); - - if (sprite) - sprite->setSize(sprite->size() + vm->getInput(0, 1)->toDouble()); - - return 1; -} - -unsigned int LooksBlocks::setSizeTo(VirtualMachine *vm) -{ - Sprite *sprite = dynamic_cast(vm->target()); - - if (sprite) - sprite->setSize(vm->getInput(0, 1)->toDouble()); - - return 1; -} - -unsigned int LooksBlocks::size(VirtualMachine *vm) -{ - Sprite *sprite = dynamic_cast(vm->target()); - - if (sprite) - vm->addReturnValue(sprite->size()); - else - vm->addReturnValue(0); - - return 0; -} - -void LooksBlocks::setCostumeByIndex(Target *target, long index) -{ - long costumeCount = target->costumes().size(); - - if (index < 0 || index >= costumeCount) { - if (index < 0) - index = std::fmod(costumeCount + std::fmod(index, -costumeCount), costumeCount); - else - index = std::fmod(index, costumeCount); - } - - target->setCostumeIndex(index); -} - -unsigned int LooksBlocks::switchCostumeTo(VirtualMachine *vm) -{ - // https://github.com/scratchfoundation/scratch-vm/blob/8dbcc1fc8f8d8c4f1e40629fe8a388149d6dfd1c/src/blocks/scratch3_looks.js#L389-L413 - Target *target = vm->target(); - - if (!target) - return 1; - - const Value *name = vm->getInput(0, 1); - - if (!name->isString()) { - // Numbers should be treated as costume indices, always - if (name->isNaN() || name->isInfinity() || name->isNegativeInfinity()) - target->setCostumeIndex(0); - else - setCostumeByIndex(target, name->toLong() - 1); - } else { - // Strings should be treated as costume names, where possible - const int costumeIndex = target->findCostume(name->toString()); - std::string nameStr = name->toString(); - - auto it = std::find_if(nameStr.begin(), nameStr.end(), [](char c) { return !std::isspace(c); }); - bool isWhiteSpace = (it == nameStr.end()); - - if (costumeIndex != -1) { - setCostumeByIndex(target, costumeIndex); - } else if (nameStr == "next costume") { - nextCostume(vm); - } else if (nameStr == "previous costume") { - previousCostume(vm); - // Try to cast the string to a number (and treat it as a costume index) - // Pure whitespace should not be treated as a number - // Note: isNaN will cast the string to a number before checking if it's NaN - } else if (!(name->isNaN() || isWhiteSpace)) { - target->setCostumeIndex(name->toInt() - 1); - } - } - - return 1; -} - -unsigned int LooksBlocks::nextCostume(VirtualMachine *vm) -{ - if (Target *target = vm->target()) - setCostumeByIndex(target, target->costumeIndex() + 1); - - return 0; -} - -unsigned int LooksBlocks::previousCostume(VirtualMachine *vm) -{ - if (Target *target = vm->target()) - setCostumeByIndex(target, target->costumeIndex() - 1); - - return 0; -} - -void LooksBlocks::startBackdropScripts(VirtualMachine *vm, bool wait) -{ - if (Stage *stage = vm->engine()->stage()) { - if (stage->costumes().size() > 0) - vm->engine()->startBackdropScripts(stage->currentCostume()->broadcast(), vm->thread(), wait); - } -} - -void LooksBlocks::switchBackdropToImpl(VirtualMachine *vm) -{ - // https://github.com/scratchfoundation/scratch-vm/blob/8dbcc1fc8f8d8c4f1e40629fe8a388149d6dfd1c/src/blocks/scratch3_looks.js#L423-L462 - Stage *stage = vm->engine()->stage(); - - if (!stage) - return; - - const Value *name = vm->getInput(0, 1); - - if (!name->isString()) { - // Numbers should be treated as costume indices, always - if (name->isNaN() || name->isInfinity() || name->isNegativeInfinity()) - stage->setCostumeIndex(0); - else - setCostumeByIndex(stage, name->toLong() - 1); - } else { - // Strings should be treated as costume names, where possible - const int costumeIndex = stage->findCostume(name->toString()); - std::string nameStr = name->toString(); - - auto it = std::find_if(nameStr.begin(), nameStr.end(), [](char c) { return !std::isspace(c); }); - bool isWhiteSpace = (it == nameStr.end()); - - if (costumeIndex != -1) { - setCostumeByIndex(stage, costumeIndex); - } else if (nameStr == "next backdrop") { - nextBackdropImpl(vm); - } else if (nameStr == "previous backdrop") { - previousBackdropImpl(vm); - } else if (nameStr == "random backdrop") { - randomBackdropImpl(vm); - // Try to cast the string to a number (and treat it as a costume index) - // Pure whitespace should not be treated as a number - // Note: isNaN will cast the string to a number before checking if it's NaN - } else if (!(name->isNaN() || isWhiteSpace)) { - stage->setCostumeIndex(name->toInt() - 1); - } - } -} - -void LooksBlocks::nextBackdropImpl(VirtualMachine *vm) -{ - if (Stage *stage = vm->engine()->stage()) - setCostumeByIndex(stage, stage->costumeIndex() + 1); -} - -void LooksBlocks::previousBackdropImpl(VirtualMachine *vm) -{ - if (Stage *stage = vm->engine()->stage()) - setCostumeByIndex(stage, stage->costumeIndex() - 1); -} - -void LooksBlocks::randomBackdropImpl(VirtualMachine *vm) -{ - if (!rng) - rng = RandomGenerator::instance().get(); - - if (Stage *stage = vm->engine()->stage()) { - std::size_t count = stage->costumes().size(); - - if (count > 1) { - stage->setCostumeIndex(rng->randintExcept(0, count - 1, stage->costumeIndex())); - } - } -} - -unsigned int LooksBlocks::switchBackdropTo(VirtualMachine *vm) -{ - switchBackdropToImpl(vm); - startBackdropScripts(vm, false); - - return 1; -} - -unsigned int LooksBlocks::switchBackdropToAndWait(VirtualMachine *vm) -{ - switchBackdropToImpl(vm); - startBackdropScripts(vm, true); - vm->promise(); - - return 1; -} - -unsigned int LooksBlocks::nextBackdrop(VirtualMachine *vm) -{ - nextBackdropImpl(vm); - startBackdropScripts(vm, false); - - return 0; -} - -unsigned int LooksBlocks::previousBackdrop(VirtualMachine *vm) -{ - previousBackdropImpl(vm); - startBackdropScripts(vm, false); - - return 0; -} - -unsigned int LooksBlocks::randomBackdrop(VirtualMachine *vm) -{ - randomBackdropImpl(vm); - startBackdropScripts(vm, false); - - return 0; -} - -unsigned int LooksBlocks::goToFront(VirtualMachine *vm) -{ - Sprite *sprite = dynamic_cast(vm->target()); - - if (sprite) - vm->engine()->moveDrawableToFront(sprite); - - return 0; -} - -unsigned int LooksBlocks::goToBack(VirtualMachine *vm) -{ - Sprite *sprite = dynamic_cast(vm->target()); - - if (sprite) - vm->engine()->moveDrawableToBack(sprite); - - return 0; -} - -unsigned int LooksBlocks::goForwardLayers(VirtualMachine *vm) -{ - Sprite *sprite = dynamic_cast(vm->target()); - - if (sprite) - vm->engine()->moveDrawableForwardLayers(sprite, vm->getInput(0, 1)->toInt()); - - return 1; -} - -unsigned int LooksBlocks::goBackwardLayers(VirtualMachine *vm) -{ - Sprite *sprite = dynamic_cast(vm->target()); - - if (sprite) - vm->engine()->moveDrawableBackwardLayers(sprite, vm->getInput(0, 1)->toInt()); - - return 1; -} - -unsigned int LooksBlocks::costumeNumber(VirtualMachine *vm) -{ - if (Target *target = vm->target()) - vm->addReturnValue(target->costumeIndex() + 1); - else - vm->addReturnValue(0); - - return 0; -} - -unsigned int LooksBlocks::costumeName(VirtualMachine *vm) -{ - if (Target *target = vm->target()) { - auto costume = target->currentCostume(); - - if (costume) - vm->addReturnValue(costume->name()); - else - vm->addReturnValue(""); - } else - vm->addReturnValue(""); - - return 0; -} - -unsigned int LooksBlocks::backdropNumber(VirtualMachine *vm) -{ - if (Stage *stage = vm->engine()->stage()) - vm->addReturnValue(stage->costumeIndex() + 1); - else - vm->addReturnValue(0); - - return 0; -} - -unsigned int LooksBlocks::backdropName(VirtualMachine *vm) -{ - if (Stage *stage = vm->engine()->stage()) { - auto costume = stage->currentCostume(); - - if (costume) - vm->addReturnValue(costume->name()); - else - vm->addReturnValue(""); - } else - vm->addReturnValue(""); - - return 0; } diff --git a/src/blocks/looksblocks.h b/src/blocks/looksblocks.h index 8881d7eb..8bfa5f18 100644 --- a/src/blocks/looksblocks.h +++ b/src/blocks/looksblocks.h @@ -3,174 +3,17 @@ #pragma once #include -#include -#include -#include -#include -#include namespace libscratchcpp { -class Stage; -class Value; -class IGraphicsEffect; -class IRandomGenerator; -class IClock; - class LooksBlocks : public IExtension { public: - enum Inputs - { - MESSAGE, - SECS, - CHANGE, - SIZE, - COSTUME, - BACKDROP, - VALUE, - NUM - }; - - enum Fields - { - NUMBER_NAME, - EFFECT, - FRONT_BACK, - FORWARD_BACKWARD - }; - - enum FieldValues - { - Number, - Name, - ColorEffect, - FisheyeEffect, - WhirlEffect, - PixelateEffect, - MosaicEffect, - BrightnessEffect, - GhostEffect, - Front, - Back, - Forward, - Backward - }; - std::string name() const override; std::string description() const override; void registerBlocks(IEngine *engine) override; - void onInit(IEngine *engine) override; - - static void compileSayForSecs(Compiler *compiler); - static void compileSay(Compiler *compiler); - static void compileThinkForSecs(Compiler *compiler); - static void compileThink(Compiler *compiler); - static void compileShow(Compiler *compiler); - static void compileHide(Compiler *compiler); - static void compileChangeEffectBy(Compiler *compiler); - static void compileSetEffectTo(Compiler *compiler); - static void compileClearGraphicEffects(Compiler *compiler); - static void compileChangeSizeBy(Compiler *compiler); - static void compileSetSizeTo(Compiler *compiler); - static void compileSize(Compiler *compiler); - static void compileSwitchCostumeTo(Compiler *compiler); - static void compileNextCostume(Compiler *compiler); - static void compileSwitchBackdropTo(Compiler *compiler); - static void compileSwitchBackdropToAndWait(Compiler *compiler); - static void compileNextBackdrop(Compiler *compiler); - static void compileGoToFrontBack(Compiler *compiler); - static void compileGoForwardBackwardLayers(Compiler *compiler); - static void compileCostumeNumberName(Compiler *compiler); - static void compileBackdropNumberName(Compiler *compiler); - - static const std::string &costumeNumberNameMonitorName(Block *block); - static const std::string &backdropNumberNameMonitorName(Block *block); - static const std::string &sizeMonitorName(Block *block); - - static void startWait(VirtualMachine *vm, double secs); - static bool wait(VirtualMachine *vm); - static void showBubble(VirtualMachine *vm, TextBubble::Type type, const std::string &text); - static void hideBubble(Target *target); - - static unsigned int startSayForSecs(VirtualMachine *vm); - static unsigned int sayForSecs(VirtualMachine *vm); - static unsigned int say(VirtualMachine *vm); - - static unsigned int startThinkForSecs(VirtualMachine *vm); - static unsigned int thinkForSecs(VirtualMachine *vm); - static unsigned int think(VirtualMachine *vm); - - static unsigned int show(VirtualMachine *vm); - static unsigned int hide(VirtualMachine *vm); - - static unsigned int changeEffectBy(VirtualMachine *vm); - static unsigned int changeColorEffectBy(VirtualMachine *vm); - static unsigned int changeFisheyeEffectBy(VirtualMachine *vm); - static unsigned int changeWhirlEffectBy(VirtualMachine *vm); - static unsigned int changePixelateEffectBy(VirtualMachine *vm); - static unsigned int changeMosaicEffectBy(VirtualMachine *vm); - static unsigned int changeBrightnessEffectBy(VirtualMachine *vm); - static unsigned int changeGhostEffectBy(VirtualMachine *vm); - - static unsigned int setEffectTo(VirtualMachine *vm); - static unsigned int setColorEffectTo(VirtualMachine *vm); - static unsigned int setFisheyeEffectTo(VirtualMachine *vm); - static unsigned int setWhirlEffectTo(VirtualMachine *vm); - static unsigned int setPixelateEffectTo(VirtualMachine *vm); - static unsigned int setMosaicEffectTo(VirtualMachine *vm); - static unsigned int setBrightnessEffectTo(VirtualMachine *vm); - static unsigned int setGhostEffectTo(VirtualMachine *vm); - - static unsigned int clearGraphicEffects(VirtualMachine *vm); - static unsigned int changeSizeBy(VirtualMachine *vm); - static unsigned int setSizeTo(VirtualMachine *vm); - static unsigned int size(VirtualMachine *vm); - - static void setCostumeByIndex(Target *target, long index); - static unsigned int switchCostumeTo(VirtualMachine *vm); - static unsigned int nextCostume(VirtualMachine *vm); - static unsigned int previousCostume(VirtualMachine *vm); - - static void startBackdropScripts(VirtualMachine *vm, bool wait); - static void switchBackdropToImpl(VirtualMachine *vm); - static void nextBackdropImpl(VirtualMachine *vm); - static void previousBackdropImpl(VirtualMachine *vm); - static void randomBackdropImpl(VirtualMachine *vm); - - static unsigned int switchBackdropTo(VirtualMachine *vm); - static unsigned int switchBackdropToAndWait(VirtualMachine *vm); - static unsigned int nextBackdrop(VirtualMachine *vm); - static unsigned int previousBackdrop(VirtualMachine *vm); - static unsigned int randomBackdrop(VirtualMachine *vm); - - static unsigned int goToFront(VirtualMachine *vm); - static unsigned int goToBack(VirtualMachine *vm); - - static unsigned int goForwardLayers(VirtualMachine *vm); - static unsigned int goBackwardLayers(VirtualMachine *vm); - - static unsigned int costumeNumber(VirtualMachine *vm); - static unsigned int costumeName(VirtualMachine *vm); - static unsigned int backdropNumber(VirtualMachine *vm); - static unsigned int backdropName(VirtualMachine *vm); - - static inline std::unordered_map> m_timeMap; - static inline std::unordered_map m_waitingBubbles; - - static inline std::vector m_customGraphicsEffects; - static inline IGraphicsEffect *m_colorEffect = nullptr; - static inline IGraphicsEffect *m_fisheyeEffect = nullptr; - static inline IGraphicsEffect *m_whirlEffect = nullptr; - static inline IGraphicsEffect *m_pixelateEffect = nullptr; - static inline IGraphicsEffect *m_mosaicEffect = nullptr; - static inline IGraphicsEffect *m_brightnessEffect = nullptr; - static inline IGraphicsEffect *m_ghostEffect = nullptr; - - static IRandomGenerator *rng; - static IClock *clock; }; } // namespace libscratchcpp diff --git a/src/blocks/motionblocks.cpp b/src/blocks/motionblocks.cpp index c5a96004..e44a673c 100644 --- a/src/blocks/motionblocks.cpp +++ b/src/blocks/motionblocks.cpp @@ -1,24 +1,9 @@ // SPDX-License-Identifier: Apache-2.0 -#include -#include -#include -#include -#include -#include -#include - #include "motionblocks.h" -#include "../engine/internal/randomgenerator.h" -#include "../engine/internal/clock.h" using namespace libscratchcpp; -static const double pi = std::acos(-1); // TODO: Use std::numbers::pi in C++20 - -IRandomGenerator *MotionBlocks::rng = nullptr; -IClock *MotionBlocks::clock = nullptr; - std::string MotionBlocks::name() const { return "Motion"; @@ -31,787 +16,4 @@ std::string MotionBlocks::description() const void MotionBlocks::registerBlocks(IEngine *engine) { - // Blocks - engine->addCompileFunction(this, "motion_movesteps", &compileMoveSteps); - engine->addCompileFunction(this, "motion_turnright", &compileTurnRight); - engine->addCompileFunction(this, "motion_turnleft", &compileTurnLeft); - engine->addCompileFunction(this, "motion_pointindirection", &compilePointInDirection); - engine->addCompileFunction(this, "motion_pointtowards", &compilePointTowards); - engine->addCompileFunction(this, "motion_gotoxy", &compileGoToXY); - engine->addCompileFunction(this, "motion_goto", &compileGoTo); - engine->addCompileFunction(this, "motion_glidesecstoxy", &compileGlideSecsToXY); - engine->addCompileFunction(this, "motion_glideto", &compileGlideTo); - engine->addCompileFunction(this, "motion_changexby", &compileChangeXBy); - engine->addCompileFunction(this, "motion_setx", &compileSetX); - engine->addCompileFunction(this, "motion_changeyby", &compileChangeYBy); - engine->addCompileFunction(this, "motion_sety", &compileSetY); - engine->addCompileFunction(this, "motion_ifonedgebounce", &compileIfOnEdgeBounce); - engine->addCompileFunction(this, "motion_setrotationstyle", &compileSetRotationStyle); - engine->addCompileFunction(this, "motion_xposition", &compileXPosition); - engine->addCompileFunction(this, "motion_yposition", &compileYPosition); - engine->addCompileFunction(this, "motion_direction", &compileDirection); - - // Monitor names - engine->addMonitorNameFunction(this, "motion_xposition", &xPositionMonitorName); - engine->addMonitorNameFunction(this, "motion_yposition", &yPositionMonitorName); - engine->addMonitorNameFunction(this, "motion_direction", &directionMonitorName); - - // Inputs - engine->addInput(this, "STEPS", STEPS); - engine->addInput(this, "DEGREES", DEGREES); - engine->addInput(this, "DIRECTION", DIRECTION); - engine->addInput(this, "TOWARDS", TOWARDS); - engine->addInput(this, "X", X); - engine->addInput(this, "Y", Y); - engine->addInput(this, "TO", TO); - engine->addInput(this, "SECS", SECS); - engine->addInput(this, "DX", DX); - engine->addInput(this, "DY", DY); - - // Fields - engine->addField(this, "STYLE", STYLE); - - // Field values - engine->addFieldValue(this, "left-right", LeftRight); - engine->addFieldValue(this, "don't rotate", DoNotRotate); - engine->addFieldValue(this, "all around", AllAround); -} - -void MotionBlocks::onInit(IEngine *engine) -{ - engine->threadAboutToStop().connect([](Thread *thread) { - m_timeMap.erase(thread->vm()); - m_glideMap.erase(thread->vm()); - }); -} - -void MotionBlocks::compileMoveSteps(Compiler *compiler) -{ - compiler->addInput(STEPS); - compiler->addFunctionCall(&moveSteps); -} - -void MotionBlocks::compileTurnRight(Compiler *compiler) -{ - compiler->addInput(DEGREES); - compiler->addFunctionCall(&turnRight); -} - -void MotionBlocks::compileTurnLeft(Compiler *compiler) -{ - compiler->addInput(DEGREES); - compiler->addFunctionCall(&turnLeft); -} - -void MotionBlocks::compilePointInDirection(Compiler *compiler) -{ - compiler->addInput(DIRECTION); - compiler->addFunctionCall(&pointInDirection); -} - -void MotionBlocks::compilePointTowards(Compiler *compiler) -{ - Input *input = compiler->input(TOWARDS); - - if (input->pointsToDropdownMenu()) { - std::string value = input->selectedMenuItem(); - - if (value == "_mouse_") - compiler->addFunctionCall(&pointTowardsMousePointer); - else if (value == "_random_") - compiler->addFunctionCall(&pointTowardsRandomPosition); - else { - int index = compiler->engine()->findTarget(value); - compiler->addConstValue(index); - compiler->addFunctionCall(&pointTowardsByIndex); - } - } else { - compiler->addInput(input); - compiler->addFunctionCall(&pointTowards); - } -} - -void MotionBlocks::compileGoToXY(Compiler *compiler) -{ - compiler->addInput(X); - compiler->addInput(Y); - compiler->addFunctionCall(&goToXY); -} - -void MotionBlocks::compileGoTo(Compiler *compiler) -{ - Input *input = compiler->input(TO); - - if (input->pointsToDropdownMenu()) { - std::string value = input->selectedMenuItem(); - - if (value == "_mouse_") - compiler->addFunctionCall(&goToMousePointer); - else if (value == "_random_") - compiler->addFunctionCall(&goToRandomPosition); - else { - int index = compiler->engine()->findTarget(value); - compiler->addConstValue(index); - compiler->addFunctionCall(&goToByIndex); - } - } else { - compiler->addInput(input); - compiler->addFunctionCall(&goTo); - } -} - -void MotionBlocks::compileGlideSecsToXY(Compiler *compiler) -{ - compiler->addInput(SECS); - compiler->addInput(X); - compiler->addInput(Y); - compiler->addFunctionCall(&startGlideSecsTo); - compiler->addFunctionCall(&glideSecsTo); -} - -void MotionBlocks::compileGlideTo(Compiler *compiler) -{ - compiler->addInput(SECS); - - Input *input = compiler->input(TO); - - if (input->pointsToDropdownMenu()) { - std::string value = input->selectedMenuItem(); - - if (value == "_mouse_") - compiler->addFunctionCall(&startGlideToMousePointer); - else if (value == "_random_") - compiler->addFunctionCall(&startGlideToRandomPosition); - else { - int index = compiler->engine()->findTarget(value); - compiler->addConstValue(index); - compiler->addFunctionCall(&startGlideToByIndex); - } - } else { - compiler->addInput(input); - compiler->addFunctionCall(&startGlideTo); - } - - compiler->addFunctionCall(&glideSecsTo); -} - -void MotionBlocks::compileChangeXBy(Compiler *compiler) -{ - compiler->addInput(DX); - compiler->addFunctionCall(&changeXBy); -} - -void MotionBlocks::compileSetX(Compiler *compiler) -{ - compiler->addInput(X); - compiler->addFunctionCall(&setX); -} - -void MotionBlocks::compileChangeYBy(Compiler *compiler) -{ - compiler->addInput(DY); - compiler->addFunctionCall(&changeYBy); -} - -void MotionBlocks::compileSetY(Compiler *compiler) -{ - compiler->addInput(Y); - compiler->addFunctionCall(&setY); -} - -void MotionBlocks::compileIfOnEdgeBounce(Compiler *compiler) -{ - compiler->addFunctionCall(&ifOnEdgeBounce); -} - -void MotionBlocks::compileSetRotationStyle(Compiler *compiler) -{ - int option = compiler->field(STYLE)->specialValueId(); - - switch (option) { - case LeftRight: - compiler->addFunctionCall(&setLeftRightRotationStyle); - break; - - case DoNotRotate: - compiler->addFunctionCall(&setDoNotRotateRotationStyle); - break; - - case AllAround: - compiler->addFunctionCall(&setAllAroundRotationStyle); - break; - - default: - break; - } -} - -void MotionBlocks::compileXPosition(Compiler *compiler) -{ - compiler->addFunctionCall(&xPosition); -} - -void MotionBlocks::compileYPosition(Compiler *compiler) -{ - compiler->addFunctionCall(&yPosition); -} - -void MotionBlocks::compileDirection(Compiler *compiler) -{ - compiler->addFunctionCall(&direction); -} - -const std::string &MotionBlocks::xPositionMonitorName(Block *block) -{ - static const std::string name = "x position"; - return name; -} - -const std::string &MotionBlocks::yPositionMonitorName(Block *block) -{ - static const std::string name = "y position"; - return name; -} - -const std::string &MotionBlocks::directionMonitorName(Block *block) -{ - static const std::string name = "direction"; - return name; -} - -unsigned int MotionBlocks::moveSteps(VirtualMachine *vm) -{ - Sprite *sprite = dynamic_cast(vm->target()); - - if (sprite) { - double dir = sprite->direction(); - double steps = vm->getInput(0, 1)->toDouble(); - sprite->setPosition(sprite->x() + std::sin(dir * pi / 180) * steps, sprite->y() + std::cos(dir * pi / 180) * steps); - } - - return 1; -} - -unsigned int MotionBlocks::turnRight(VirtualMachine *vm) -{ - Sprite *sprite = dynamic_cast(vm->target()); - - if (sprite) - sprite->setDirection(sprite->direction() + vm->getInput(0, 1)->toDouble()); - - return 1; -} - -unsigned int MotionBlocks::turnLeft(VirtualMachine *vm) -{ - Sprite *sprite = dynamic_cast(vm->target()); - - if (sprite) - sprite->setDirection(sprite->direction() - vm->getInput(0, 1)->toDouble()); - - return 1; -} - -unsigned int MotionBlocks::pointInDirection(VirtualMachine *vm) -{ - Sprite *sprite = dynamic_cast(vm->target()); - - if (sprite) - sprite->setDirection(vm->getInput(0, 1)->toDouble()); - - return 1; -} - -void MotionBlocks::pointTowardsPos(Sprite *sprite, double x, double y) -{ - if (!sprite) - return; - - // https://en.scratch-wiki.info/wiki/Point_Towards_()_(block)#Workaround - double deltaX = x - sprite->x(); - double deltaY = y - sprite->y(); - - if (deltaY == 0) { - if (deltaX < 0) - sprite->setDirection(-90); - else - sprite->setDirection(90); - } else if (deltaY < 0) - sprite->setDirection(180 + (180 / pi) * std::atan(deltaX / deltaY)); - else - sprite->setDirection((180 / pi) * std::atan(deltaX / deltaY)); -} - -unsigned int MotionBlocks::pointTowards(VirtualMachine *vm) -{ - std::string value = vm->getInput(0, 1)->toString(); - - if (value == "_mouse_") - pointTowardsPos(dynamic_cast(vm->target()), vm->engine()->mouseX(), vm->engine()->mouseY()); - else if (value == "_random_") { - const unsigned int stageWidth = vm->engine()->stageWidth(); - const unsigned int stageHeight = vm->engine()->stageHeight(); - - if (!rng) - rng = RandomGenerator::instance().get(); - - pointTowardsPos(dynamic_cast(vm->target()), rng->randint(-static_cast(stageWidth / 2), stageWidth / 2), rng->randint(-static_cast(stageHeight / 2), stageHeight / 2)); - } else { - Target *target = vm->engine()->targetAt(vm->engine()->findTarget(value)); - Sprite *sprite = dynamic_cast(target); - - if (sprite) - pointTowardsPos(dynamic_cast(vm->target()), sprite->x(), sprite->y()); - } - - return 1; -} - -unsigned int MotionBlocks::pointTowardsByIndex(VirtualMachine *vm) -{ - Target *target = vm->engine()->targetAt(vm->getInput(0, 1)->toInt()); - Sprite *sprite = dynamic_cast(target); - - if (sprite) - pointTowardsPos(dynamic_cast(vm->target()), sprite->x(), sprite->y()); - - return 1; -} - -unsigned int MotionBlocks::pointTowardsMousePointer(VirtualMachine *vm) -{ - pointTowardsPos(dynamic_cast(vm->target()), vm->engine()->mouseX(), vm->engine()->mouseY()); - return 0; -} - -unsigned int MotionBlocks::pointTowardsRandomPosition(VirtualMachine *vm) -{ - const unsigned int stageWidth = vm->engine()->stageWidth(); - const unsigned int stageHeight = vm->engine()->stageHeight(); - - if (!rng) - rng = RandomGenerator::instance().get(); - - pointTowardsPos(dynamic_cast(vm->target()), rng->randint(-static_cast(stageWidth / 2), stageWidth / 2), rng->randint(-static_cast(stageHeight / 2), stageHeight / 2)); - - return 0; -} - -unsigned int MotionBlocks::goToXY(VirtualMachine *vm) -{ - Sprite *sprite = dynamic_cast(vm->target()); - - if (sprite) { - sprite->setPosition(vm->getInput(0, 2)->toDouble(), vm->getInput(1, 2)->toDouble()); - } - - return 2; -} - -unsigned int MotionBlocks::goTo(VirtualMachine *vm) -{ - Sprite *sprite = dynamic_cast(vm->target()); - - if (!sprite) - return 1; - - std::string value = vm->getInput(0, 1)->toString(); - - if (value == "_mouse_") - sprite->setPosition(vm->engine()->mouseX(), vm->engine()->mouseY()); - else if (value == "_random_") { - const unsigned int stageWidth = vm->engine()->stageWidth(); - const unsigned int stageHeight = vm->engine()->stageHeight(); - - if (!rng) - rng = RandomGenerator::instance().get(); - - sprite->setPosition(rng->randint(-static_cast(stageWidth / 2), stageWidth / 2), rng->randint(-static_cast(stageHeight / 2), stageHeight / 2)); - } else { - Target *target = vm->engine()->targetAt(vm->engine()->findTarget(value)); - Sprite *targetSprite = dynamic_cast(target); - - if (targetSprite) - sprite->setPosition(targetSprite->x(), targetSprite->y()); - } - - return 1; -} - -unsigned int MotionBlocks::goToByIndex(VirtualMachine *vm) -{ - Sprite *sprite = dynamic_cast(vm->target()); - Target *target = vm->engine()->targetAt(vm->getInput(0, 1)->toInt()); - Sprite *targetSprite = dynamic_cast(target); - - if (sprite && targetSprite) - sprite->setPosition(targetSprite->x(), targetSprite->y()); - - return 1; -} - -unsigned int MotionBlocks::goToMousePointer(VirtualMachine *vm) -{ - Sprite *sprite = dynamic_cast(vm->target()); - - if (sprite) - sprite->setPosition(vm->engine()->mouseX(), vm->engine()->mouseY()); - - return 0; -} - -unsigned int MotionBlocks::goToRandomPosition(VirtualMachine *vm) -{ - Sprite *sprite = dynamic_cast(vm->target()); - - if (sprite) { - const unsigned int stageWidth = vm->engine()->stageWidth(); - const unsigned int stageHeight = vm->engine()->stageHeight(); - - if (!rng) - rng = RandomGenerator::instance().get(); - - sprite->setPosition(rng->randint(-static_cast(stageWidth / 2), stageWidth / 2), rng->randint(-static_cast(stageHeight / 2), stageHeight / 2)); - } - - return 0; -} - -void MotionBlocks::startGlidingToPos(VirtualMachine *vm, double x, double y, double secs) -{ - Sprite *sprite = dynamic_cast(vm->target()); - - if (!sprite) - return; - - if (secs <= 0) { - if (sprite) - sprite->setPosition(x, y); - - return; - } - - if (!clock) - clock = Clock::instance().get(); - - auto currentTime = clock->currentSteadyTime(); - m_timeMap[vm] = { currentTime, secs * 1000 }; - m_glideMap[vm] = { { sprite->x(), sprite->y() }, { x, y } }; -} - -void MotionBlocks::continueGliding(VirtualMachine *vm) -{ - if (!clock) - clock = Clock::instance().get(); - - auto currentTime = clock->currentSteadyTime(); - auto elapsedTime = std::chrono::duration_cast(currentTime - m_timeMap[vm].first).count(); - auto maxTime = m_timeMap[vm].second; - assert(m_timeMap.count(vm) == 1); - - Sprite *sprite = dynamic_cast(vm->target()); - double x = m_glideMap[vm].second.first; - double y = m_glideMap[vm].second.second; - - if (elapsedTime >= maxTime) { - if (sprite) - sprite->setPosition(x, y); - - m_timeMap.erase(vm); - m_glideMap.erase(vm); - } else { - if (sprite) { - double startX = m_glideMap[vm].first.first; - double startY = m_glideMap[vm].first.second; - double factor = elapsedTime / static_cast(maxTime); - assert(factor >= 0 && factor < 1); - - sprite->setPosition(startX + (x - startX) * factor, startY + (y - startY) * factor); - } - - vm->stop(true, true, true); - } -} - -unsigned int MotionBlocks::startGlideSecsTo(VirtualMachine *vm) -{ - startGlidingToPos(vm, vm->getInput(1, 3)->toDouble(), vm->getInput(2, 3)->toDouble(), vm->getInput(0, 3)->toDouble()); - return 3; -} - -unsigned int MotionBlocks::glideSecsTo(VirtualMachine *vm) -{ - if (m_timeMap.find(vm) != m_timeMap.cend()) { - assert(m_glideMap.find(vm) != m_glideMap.cend()); - continueGliding(vm); - } - - return 0; -} - -unsigned int MotionBlocks::startGlideTo(VirtualMachine *vm) -{ - Sprite *sprite = dynamic_cast(vm->target()); - - if (!sprite) - return 1; - - std::string value = vm->getInput(1, 2)->toString(); - - if (value == "_mouse_") - startGlidingToPos(vm, vm->engine()->mouseX(), vm->engine()->mouseY(), vm->getInput(0, 2)->toDouble()); - else if (value == "_random_") { - const unsigned int stageWidth = vm->engine()->stageWidth(); - const unsigned int stageHeight = vm->engine()->stageHeight(); - - if (!rng) - rng = RandomGenerator::instance().get(); - - startGlidingToPos(vm, rng->randint(-static_cast(stageWidth / 2), stageWidth / 2), rng->randint(-static_cast(stageHeight / 2), stageHeight / 2), vm->getInput(0, 2)->toDouble()); - } else { - Target *target = vm->engine()->targetAt(vm->engine()->findTarget(value)); - Sprite *targetSprite = dynamic_cast(target); - - if (targetSprite) - startGlidingToPos(vm, targetSprite->x(), targetSprite->y(), vm->getInput(0, 2)->toDouble()); - } - - return 2; -} - -unsigned int MotionBlocks::startGlideToByIndex(VirtualMachine *vm) -{ - Sprite *sprite = dynamic_cast(vm->target()); - Target *target = vm->engine()->targetAt(vm->getInput(1, 2)->toInt()); - Sprite *targetSprite = dynamic_cast(target); - - if (sprite && targetSprite) - startGlidingToPos(vm, targetSprite->x(), targetSprite->y(), vm->getInput(0, 2)->toDouble()); - - return 2; -} - -unsigned int MotionBlocks::startGlideToMousePointer(VirtualMachine *vm) -{ - Sprite *sprite = dynamic_cast(vm->target()); - - if (sprite) - startGlidingToPos(vm, vm->engine()->mouseX(), vm->engine()->mouseY(), vm->getInput(0, 1)->toDouble()); - - return 1; -} - -unsigned int MotionBlocks::startGlideToRandomPosition(VirtualMachine *vm) -{ - Sprite *sprite = dynamic_cast(vm->target()); - - if (sprite) { - const unsigned int stageWidth = vm->engine()->stageWidth(); - const unsigned int stageHeight = vm->engine()->stageHeight(); - - if (!rng) - rng = RandomGenerator::instance().get(); - - startGlidingToPos(vm, rng->randint(-static_cast(stageWidth / 2), stageWidth / 2), rng->randint(-static_cast(stageHeight / 2), stageHeight / 2), vm->getInput(0, 1)->toDouble()); - } - - return 1; -} - -unsigned int MotionBlocks::changeXBy(VirtualMachine *vm) -{ - Sprite *sprite = dynamic_cast(vm->target()); - - if (sprite) - sprite->setX(sprite->x() + vm->getInput(0, 1)->toDouble()); - - return 1; -} - -unsigned int MotionBlocks::setX(VirtualMachine *vm) -{ - Sprite *sprite = dynamic_cast(vm->target()); - - if (sprite) - sprite->setX(vm->getInput(0, 1)->toDouble()); - - return 1; -} - -unsigned int MotionBlocks::changeYBy(VirtualMachine *vm) -{ - Sprite *sprite = dynamic_cast(vm->target()); - - if (sprite) - sprite->setY(sprite->y() + vm->getInput(0, 1)->toDouble()); - - return 1; -} - -unsigned int MotionBlocks::setY(VirtualMachine *vm) -{ - Sprite *sprite = dynamic_cast(vm->target()); - - if (sprite) - sprite->setY(vm->getInput(0, 1)->toDouble()); - - return 1; -} - -unsigned int MotionBlocks::ifOnEdgeBounce(VirtualMachine *vm) -{ - // See https://github.com/scratchfoundation/scratch-vm/blob/c37745e97e6d8a77ad1dc31a943ea728dd17ba78/src/blocks/scratch3_motion.js#L186-L240 - Sprite *sprite = dynamic_cast(vm->target()); - IEngine *engine = vm->engine(); - - if (!sprite || !engine) - return 0; - - Rect bounds = sprite->boundingRect(); - - // Measure distance to edges - // Values are zero when the sprite is beyond - double stageWidth = engine->stageWidth(); - double stageHeight = engine->stageHeight(); - double distLeft = std::max(0.0, (stageWidth / 2.0) + bounds.left()); - double distTop = std::max(0.0, (stageHeight / 2.0) - bounds.top()); - double distRight = std::max(0.0, (stageWidth / 2.0) - bounds.right()); - double distBottom = std::max(0.0, (stageHeight / 2.0) + bounds.bottom()); - - // Find the nearest edge - // 1 - left - // 2 - top - // 3 - right - // 4 - bottom - unsigned short nearestEdge = 0; - double minDist = std::numeric_limits::infinity(); - - if (distLeft < minDist) { - minDist = distLeft; - nearestEdge = 1; - } - - if (distTop < minDist) { - minDist = distTop; - nearestEdge = 2; - } - - if (distRight < minDist) { - minDist = distRight; - nearestEdge = 3; - } - - if (distBottom < minDist) { - minDist = distBottom; - nearestEdge = 4; - } - - if (minDist > 0) { - return 0; // Not touching any edge - } - - assert(nearestEdge != 0); - - // Point away from the nearest edge - double radians = (90 - sprite->direction()) * pi / 180; - double dx = std::cos(radians); - double dy = -std::sin(radians); - - switch (nearestEdge) { - case 1: - // Left - dx = std::max(0.2, std::abs(dx)); - break; - - case 2: - // Top - dy = std::max(0.2, std::abs(dy)); - break; - - case 3: - // Right - dx = 0 - std::max(0.2, std::abs(dx)); - break; - - case 4: - // Bottom - dy = 0 - std::max(0.2, std::abs(dy)); - break; - } - - double newDirection = (180 / pi) * (std::atan2(dy, dx)) + 90; - sprite->setDirection(newDirection); - - // Keep within the stage - double fencedX, fencedY; - sprite->keepInFence(sprite->x(), sprite->y(), &fencedX, &fencedY); - sprite->setPosition(fencedX, fencedY); - - return 0; -} - -unsigned int MotionBlocks::setLeftRightRotationStyle(VirtualMachine *vm) -{ - Sprite *sprite = dynamic_cast(vm->target()); - - if (sprite) - sprite->setRotationStyle(Sprite::RotationStyle::LeftRight); - - return 0; -} - -unsigned int MotionBlocks::setDoNotRotateRotationStyle(VirtualMachine *vm) -{ - Sprite *sprite = dynamic_cast(vm->target()); - - if (sprite) - sprite->setRotationStyle(Sprite::RotationStyle::DoNotRotate); - - return 0; -} - -unsigned int MotionBlocks::setAllAroundRotationStyle(VirtualMachine *vm) -{ - Sprite *sprite = dynamic_cast(vm->target()); - - if (sprite) - sprite->setRotationStyle(Sprite::RotationStyle::AllAround); - - return 0; -} - -unsigned int MotionBlocks::xPosition(VirtualMachine *vm) -{ - Sprite *sprite = dynamic_cast(vm->target()); - - if (sprite) - vm->addReturnValue(sprite->x()); - else - vm->addReturnValue(0); - - return 0; -} - -unsigned int MotionBlocks::yPosition(VirtualMachine *vm) -{ - Sprite *sprite = dynamic_cast(vm->target()); - - if (sprite) - vm->addReturnValue(sprite->y()); - else - vm->addReturnValue(0); - - return 0; -} - -unsigned int MotionBlocks::direction(VirtualMachine *vm) -{ - Sprite *sprite = dynamic_cast(vm->target()); - - if (sprite) - vm->addReturnValue(sprite->direction()); - else - vm->addReturnValue(0); - - return 0; } diff --git a/src/blocks/motionblocks.h b/src/blocks/motionblocks.h index b545822e..125b4073 100644 --- a/src/blocks/motionblocks.h +++ b/src/blocks/motionblocks.h @@ -2,124 +2,18 @@ #pragma once -#include -#include - #include namespace libscratchcpp { -class Sprite; -class IRandomGenerator; -class IClock; - class MotionBlocks : public IExtension { public: - enum Inputs - { - STEPS, - DEGREES, - DIRECTION, - TOWARDS, - X, - Y, - TO, - SECS, - DX, - DY - }; - - enum Fields - { - STYLE - }; - - enum FieldValues - { - LeftRight, - DoNotRotate, - AllAround - }; - std::string name() const override; std::string description() const override; void registerBlocks(IEngine *engine) override; - void onInit(IEngine *engine) override; - - static void compileMoveSteps(Compiler *compiler); - static void compileTurnRight(Compiler *compiler); - static void compileTurnLeft(Compiler *compiler); - static void compilePointInDirection(Compiler *compiler); - static void compilePointTowards(Compiler *compiler); - static void compileGoToXY(Compiler *compiler); - static void compileGoTo(Compiler *compiler); - static void compileGlideSecsToXY(Compiler *compiler); - static void compileGlideTo(Compiler *compiler); - static void compileChangeXBy(Compiler *compiler); - static void compileSetX(Compiler *compiler); - static void compileChangeYBy(Compiler *compiler); - static void compileSetY(Compiler *compiler); - static void compileIfOnEdgeBounce(Compiler *compiler); - static void compileSetRotationStyle(Compiler *compiler); - static void compileXPosition(Compiler *compiler); - static void compileYPosition(Compiler *compiler); - static void compileDirection(Compiler *compiler); - - static const std::string &xPositionMonitorName(Block *block); - static const std::string &yPositionMonitorName(Block *block); - static const std::string &directionMonitorName(Block *block); - - static unsigned int moveSteps(VirtualMachine *vm); - static unsigned int turnRight(VirtualMachine *vm); - static unsigned int turnLeft(VirtualMachine *vm); - static unsigned int pointInDirection(VirtualMachine *vm); - - static void pointTowardsPos(Sprite *sprite, double x, double y); - - static unsigned int pointTowards(VirtualMachine *vm); - static unsigned int pointTowardsByIndex(VirtualMachine *vm); - static unsigned int pointTowardsMousePointer(VirtualMachine *vm); - static unsigned int pointTowardsRandomPosition(VirtualMachine *vm); - - static unsigned int goToXY(VirtualMachine *vm); - static unsigned int goTo(VirtualMachine *vm); - static unsigned int goToByIndex(VirtualMachine *vm); - static unsigned int goToMousePointer(VirtualMachine *vm); - static unsigned int goToRandomPosition(VirtualMachine *vm); - - static void startGlidingToPos(VirtualMachine *vm, double x, double y, double secs); - static void continueGliding(VirtualMachine *vm); - - static unsigned int startGlideSecsTo(VirtualMachine *vm); - static unsigned int glideSecsTo(VirtualMachine *vm); - static unsigned int startGlideTo(VirtualMachine *vm); - static unsigned int startGlideToByIndex(VirtualMachine *vm); - static unsigned int startGlideToMousePointer(VirtualMachine *vm); - static unsigned int startGlideToRandomPosition(VirtualMachine *vm); - - static unsigned int changeXBy(VirtualMachine *vm); - static unsigned int setX(VirtualMachine *vm); - static unsigned int changeYBy(VirtualMachine *vm); - static unsigned int setY(VirtualMachine *vm); - - static unsigned int ifOnEdgeBounce(VirtualMachine *vm); - - static unsigned int setLeftRightRotationStyle(VirtualMachine *vm); - static unsigned int setDoNotRotateRotationStyle(VirtualMachine *vm); - static unsigned int setAllAroundRotationStyle(VirtualMachine *vm); - - static unsigned int xPosition(VirtualMachine *vm); - static unsigned int yPosition(VirtualMachine *vm); - static unsigned int direction(VirtualMachine *vm); - - static IRandomGenerator *rng; - static IClock *clock; - - static inline std::unordered_map> m_timeMap; - static inline std::unordered_map, std::pair>> m_glideMap; // start pos, end pos }; } // namespace libscratchcpp diff --git a/src/blocks/operatorblocks.cpp b/src/blocks/operatorblocks.cpp index 376ae27a..ef37ce9c 100644 --- a/src/blocks/operatorblocks.cpp +++ b/src/blocks/operatorblocks.cpp @@ -2,7 +2,10 @@ #include #include +#include #include +#include +#include #include "operatorblocks.h" @@ -20,15 +23,14 @@ std::string OperatorBlocks::description() const void OperatorBlocks::registerBlocks(IEngine *engine) { - // Blocks engine->addCompileFunction(this, "operator_add", &compileAdd); engine->addCompileFunction(this, "operator_subtract", &compileSubtract); engine->addCompileFunction(this, "operator_multiply", &compileMultiply); engine->addCompileFunction(this, "operator_divide", &compileDivide); - engine->addCompileFunction(this, "operator_random", &compilePickRandom); - engine->addCompileFunction(this, "operator_lt", &compileLessThan); + engine->addCompileFunction(this, "operator_random", &compileRandom); + engine->addCompileFunction(this, "operator_lt", &compileLt); engine->addCompileFunction(this, "operator_equals", &compileEquals); - engine->addCompileFunction(this, "operator_gt", &compileGreaterThan); + engine->addCompileFunction(this, "operator_gt", &compileGt); engine->addCompileFunction(this, "operator_and", &compileAnd); engine->addCompileFunction(this, "operator_or", &compileOr); engine->addCompileFunction(this, "operator_not", &compileNot); @@ -39,268 +41,188 @@ void OperatorBlocks::registerBlocks(IEngine *engine) engine->addCompileFunction(this, "operator_mod", &compileMod); engine->addCompileFunction(this, "operator_round", &compileRound); engine->addCompileFunction(this, "operator_mathop", &compileMathOp); - - // Inputs - engine->addInput(this, "NUM1", NUM1); - engine->addInput(this, "NUM2", NUM2); - engine->addInput(this, "FROM", FROM); - engine->addInput(this, "TO", TO); - engine->addInput(this, "OPERAND1", OPERAND1); - engine->addInput(this, "OPERAND2", OPERAND2); - engine->addInput(this, "OPERAND", OPERAND); - engine->addInput(this, "STRING1", STRING1); - engine->addInput(this, "STRING2", STRING2); - engine->addInput(this, "LETTER", LETTER); - engine->addInput(this, "STRING", STRING); - engine->addInput(this, "NUM", NUM); - - // Fields - engine->addField(this, "OPERATOR", OPERATOR); - - // Field values - engine->addFieldValue(this, "abs", Abs); - engine->addFieldValue(this, "floor", Floor); - engine->addFieldValue(this, "ceiling", Ceiling); - engine->addFieldValue(this, "sqrt", Sqrt); - engine->addFieldValue(this, "sin", Sin); - engine->addFieldValue(this, "cos", Cos); - engine->addFieldValue(this, "tan", Tan); - engine->addFieldValue(this, "asin", Asin); - engine->addFieldValue(this, "acos", Acos); - engine->addFieldValue(this, "atan", Atan); - engine->addFieldValue(this, "ln", Ln); - engine->addFieldValue(this, "log", Log); - engine->addFieldValue(this, "e ^", Eexp); - engine->addFieldValue(this, "10 ^", Op_10exp); } -void OperatorBlocks::compileAdd(Compiler *compiler) +CompilerValue *OperatorBlocks::compileAdd(Compiler *compiler) { - compiler->addInput(NUM1); - compiler->addInput(NUM2); - compiler->addInstruction(vm::OP_ADD); + return compiler->createAdd(compiler->addInput("NUM1"), compiler->addInput("NUM2")); } -void OperatorBlocks::compileSubtract(Compiler *compiler) +CompilerValue *OperatorBlocks::compileSubtract(Compiler *compiler) { - compiler->addInput(NUM1); - compiler->addInput(NUM2); - compiler->addInstruction(vm::OP_SUBTRACT); + return compiler->createSub(compiler->addInput("NUM1"), compiler->addInput("NUM2")); } -void OperatorBlocks::compileMultiply(Compiler *compiler) +CompilerValue *OperatorBlocks::compileMultiply(Compiler *compiler) { - compiler->addInput(NUM1); - compiler->addInput(NUM2); - compiler->addInstruction(vm::OP_MULTIPLY); + return compiler->createMul(compiler->addInput("NUM1"), compiler->addInput("NUM2")); } -void OperatorBlocks::compileDivide(Compiler *compiler) +CompilerValue *OperatorBlocks::compileDivide(Compiler *compiler) { - compiler->addInput(NUM1); - compiler->addInput(NUM2); - compiler->addInstruction(vm::OP_DIVIDE); + return compiler->createDiv(compiler->addInput("NUM1"), compiler->addInput("NUM2")); } -void OperatorBlocks::compilePickRandom(Compiler *compiler) +CompilerValue *OperatorBlocks::compileRandom(Compiler *compiler) { - compiler->addInput(FROM); - compiler->addInput(TO); - compiler->addInstruction(vm::OP_RANDOM); + auto from = compiler->addInput("FROM"); + auto to = compiler->addInput("TO"); + return compiler->createRandom(from, to); } -void OperatorBlocks::compileLessThan(Compiler *compiler) +CompilerValue *OperatorBlocks::compileLt(Compiler *compiler) { - compiler->addInput(OPERAND1); - compiler->addInput(OPERAND2); - compiler->addInstruction(vm::OP_LESS_THAN); + return compiler->createCmpLT(compiler->addInput("OPERAND1"), compiler->addInput("OPERAND2")); } -void OperatorBlocks::compileEquals(Compiler *compiler) +CompilerValue *OperatorBlocks::compileEquals(Compiler *compiler) { - compiler->addInput(OPERAND1); - compiler->addInput(OPERAND2); - compiler->addInstruction(vm::OP_EQUALS); + return compiler->createCmpEQ(compiler->addInput("OPERAND1"), compiler->addInput("OPERAND2")); } -void OperatorBlocks::compileGreaterThan(Compiler *compiler) +CompilerValue *OperatorBlocks::compileGt(Compiler *compiler) { - compiler->addInput(OPERAND1); - compiler->addInput(OPERAND2); - compiler->addInstruction(vm::OP_GREATER_THAN); + return compiler->createCmpGT(compiler->addInput("OPERAND1"), compiler->addInput("OPERAND2")); } -void OperatorBlocks::compileAnd(Compiler *compiler) +CompilerValue *OperatorBlocks::compileAnd(Compiler *compiler) { - compiler->addInput(OPERAND1); - compiler->addInput(OPERAND2); - compiler->addInstruction(vm::OP_AND); + return compiler->createAnd(compiler->addInput("OPERAND1"), compiler->addInput("OPERAND2")); } -void OperatorBlocks::compileOr(Compiler *compiler) +CompilerValue *OperatorBlocks::compileOr(Compiler *compiler) { - compiler->addInput(OPERAND1); - compiler->addInput(OPERAND2); - compiler->addInstruction(vm::OP_OR); + return compiler->createOr(compiler->addInput("OPERAND1"), compiler->addInput("OPERAND2")); } -void OperatorBlocks::compileNot(Compiler *compiler) +CompilerValue *OperatorBlocks::compileNot(Compiler *compiler) { - compiler->addInput(OPERAND); - compiler->addInstruction(vm::OP_NOT); + return compiler->createNot(compiler->addInput("OPERAND")); } -void OperatorBlocks::compileJoin(Compiler *compiler) +CompilerValue *OperatorBlocks::compileJoin(Compiler *compiler) { - compiler->addInput(STRING1); - compiler->addInput(STRING2); - compiler->addInstruction(vm::OP_STR_CONCAT); + auto string1 = compiler->addInput("STRING1"); + auto string2 = compiler->addInput("STRING2"); + return compiler->addFunctionCall("operator_join", Compiler::StaticType::String, { Compiler::StaticType::String, Compiler::StaticType::String }, { string1, string2 }); } -void OperatorBlocks::compileLetterOf(Compiler *compiler) +CompilerValue *OperatorBlocks::compileLetterOf(Compiler *compiler) { - compiler->addInput(STRING); - compiler->addInput(LETTER); - compiler->addInstruction(vm::OP_STR_AT); + auto letter = compiler->addInput("LETTER"); + auto string = compiler->addInput("STRING"); + return compiler->addFunctionCall("operator_letter_of", Compiler::StaticType::String, { Compiler::StaticType::Number, Compiler::StaticType::String }, { letter, string }); } -void OperatorBlocks::compileLength(Compiler *compiler) +CompilerValue *OperatorBlocks::compileLength(Compiler *compiler) { - compiler->addInput(STRING); - compiler->addInstruction(vm::OP_STR_LENGTH); + auto string = compiler->addInput("STRING"); + return compiler->addFunctionCall("operator_length", Compiler::StaticType::Number, { Compiler::StaticType::String }, { string }); } -void OperatorBlocks::compileContains(Compiler *compiler) +CompilerValue *OperatorBlocks::compileContains(Compiler *compiler) { - compiler->addInput(STRING1); - compiler->addInput(STRING2); - compiler->addInstruction(vm::OP_STR_CONTAINS); + auto string1 = compiler->addInput("STRING1"); + auto string2 = compiler->addInput("STRING2"); + return compiler->addFunctionCall("operator_contains", Compiler::StaticType::Bool, { Compiler::StaticType::String, Compiler::StaticType::String }, { string1, string2 }); } -void OperatorBlocks::compileMod(Compiler *compiler) +CompilerValue *OperatorBlocks::compileMod(Compiler *compiler) { - compiler->addInput(NUM1); - compiler->addInput(NUM2); - compiler->addInstruction(vm::OP_MOD); + return compiler->createMod(compiler->addInput("NUM1"), compiler->addInput("NUM2")); } -void OperatorBlocks::compileRound(Compiler *compiler) +CompilerValue *OperatorBlocks::compileRound(Compiler *compiler) { - compiler->addInput(NUM); - compiler->addInstruction(vm::OP_ROUND); + return compiler->createRound(compiler->addInput("NUM")); } -void OperatorBlocks::compileMathOp(Compiler *compiler) +CompilerValue *OperatorBlocks::compileMathOp(Compiler *compiler) { - compiler->addInput(NUM); - - int id = compiler->field(OPERATOR)->specialValueId(); - switch (id) { - case Abs: - compiler->addInstruction(vm::OP_ABS); - break; - - case Floor: - compiler->addInstruction(vm::OP_FLOOR); - break; - - case Ceiling: - compiler->addInstruction(vm::OP_CEIL); - break; - - case Sqrt: - compiler->addInstruction(vm::OP_SQRT); - break; - - case Sin: - compiler->addInstruction(vm::OP_SIN); - break; - - case Cos: - compiler->addInstruction(vm::OP_COS); - break; - - case Tan: - compiler->addInstruction(vm::OP_TAN); - break; + Field *opField = compiler->field("OPERATOR"); + const std::string numInput = "NUM"; + const std::string &op = opField->value().toString(); - case Asin: - compiler->addInstruction(vm::OP_ASIN); - break; + if (op == "abs") + return compiler->createAbs(compiler->addInput(numInput)); + else if (op == "floor") + return compiler->createFloor(compiler->addInput(numInput)); + else if (op == "ceiling") + return compiler->createCeil(compiler->addInput(numInput)); + else if (op == "sqrt") + return compiler->createSqrt(compiler->addInput(numInput)); + else if (op == "sin") + return compiler->createSin(compiler->addInput(numInput)); + else if (op == "cos") + return compiler->createCos(compiler->addInput(numInput)); + else if (op == "tan") + return compiler->createTan(compiler->addInput(numInput)); + else if (op == "asin") + return compiler->createAsin(compiler->addInput(numInput)); + else if (op == "acos") + return compiler->createAcos(compiler->addInput(numInput)); + else if (op == "atan") + return compiler->createAtan(compiler->addInput(numInput)); + else if (op == "ln") + return compiler->createLn(compiler->addInput(numInput)); + else if (op == "log") + return compiler->createLog10(compiler->addInput(numInput)); + else if (op == "e ^") + return compiler->createExp(compiler->addInput(numInput)); + else if (op == "10 ^") + return compiler->createExp10(compiler->addInput(numInput)); + else + return compiler->addConstValue(Value()); +} - case Acos: - compiler->addInstruction(vm::OP_ACOS); - break; +extern "C" char *operator_join(const char *string1, const char *string2) +{ + const size_t len1 = strlen(string1); + const size_t len2 = strlen(string2); - case Atan: - compiler->addInstruction(vm::OP_ATAN); - break; + char *ret = (char *)malloc((len1 + len2 + 1) * sizeof(char)); + size_t i; - case Ln: - compiler->addFunctionCall(&op_ln); - break; + for (i = 0; i < len1; i++) + ret[i] = string1[i]; - case Log: - compiler->addFunctionCall(&op_log); - break; + for (i = 0; i < len2 + 1; i++) // +1: null-terminate + ret[len1 + i] = string2[i]; - case Eexp: - compiler->addFunctionCall(&op_eexp); - break; + return ret; +} - case Op_10exp: - compiler->addFunctionCall(&op_10exp); - break; +extern "C" char *operator_letter_of(double letter, const char *string) +{ + const size_t len = strlen(string); - default: - break; + if (letter < 1 || letter > len) { + char *ret = (char *)malloc(sizeof(char)); + ret[0] = '\0'; + return ret; } -} -unsigned int OperatorBlocks::op_ln(VirtualMachine *vm) -{ - const Value &v = *vm->getInput(0, 1); - if (v < 0) - vm->replaceReturnValue(std::numeric_limits::quiet_NaN(), 1); - else if (v == 0 || v.isNaN()) - vm->replaceReturnValue(-std::numeric_limits::infinity(), 1); - else if (!v.isInfinity()) - vm->replaceReturnValue(std::log(v.toDouble()), 1); - return 0; -} + // TODO: Rewrite this + std::u16string u16 = utf8::utf8to16(std::string(string)); + std::string str = utf8::utf16to8(std::u16string({ u16[(size_t)letter - 1] })); + char *ret = (char *)malloc((str.size() + 1) * sizeof(char)); + strcpy(ret, str.c_str()); -unsigned int OperatorBlocks::op_log(VirtualMachine *vm) -{ - const Value &v = *vm->getInput(0, 1); - if (v < 0) - vm->replaceReturnValue(std::numeric_limits::quiet_NaN(), 1); - else if (v == 0 || v.isNaN()) - vm->replaceReturnValue(-std::numeric_limits::infinity(), 1); - else if (!v.isInfinity()) - vm->replaceReturnValue(std::log10(v.toDouble()), 1); - return 0; + return ret; } -unsigned int OperatorBlocks::op_eexp(VirtualMachine *vm) +extern "C" double operator_length(const char *string) { - const Value *v = vm->getInput(0, 1); - if (v->isNaN()) - vm->replaceReturnValue(1, 1); - else if (v->isNegativeInfinity()) - vm->replaceReturnValue(0, 1); - else if (!v->isInfinity()) - vm->replaceReturnValue(std::exp(v->toDouble()), 1); - return 0; + // TODO: Rewrite this + return utf8::utf8to16(std::string(string)).size(); } -unsigned int OperatorBlocks::op_10exp(VirtualMachine *vm) +extern "C" bool operator_contains(const char *string1, const char *string2) { - const Value *v = vm->getInput(0, 1); - if (v->isNaN()) - vm->replaceReturnValue(1, 1); - else if (v->isNegativeInfinity()) - vm->replaceReturnValue(0, 1); - else if (!v->isInfinity()) - vm->replaceReturnValue(std::pow(10, v->toDouble()), 1); - return 0; + // TODO: Rewrite this + std::u16string u16string1 = utf8::utf8to16(std::string(string1)); + std::u16string u16string2 = utf8::utf8to16(std::string(string2)); + std::transform(u16string1.begin(), u16string1.end(), u16string1.begin(), ::tolower); + std::transform(u16string2.begin(), u16string2.end(), u16string2.begin(), ::tolower); + return (u16string1.find(u16string2) != std::u16string::npos); } diff --git a/src/blocks/operatorblocks.h b/src/blocks/operatorblocks.h index e9dd932a..1804e931 100644 --- a/src/blocks/operatorblocks.h +++ b/src/blocks/operatorblocks.h @@ -7,80 +7,35 @@ namespace libscratchcpp { -class Compiler; -class VirtualMachine; +class IRandomGenerator; -/*! \brief The OperatorBlocks class contains the implementation of operator blocks. */ class OperatorBlocks : public IExtension { public: - enum Inputs - { - NUM1, - NUM2, - FROM, - TO, - OPERAND1, - OPERAND2, - OPERAND, - STRING1, - STRING2, - LETTER, - STRING, - NUM - }; - - enum Fields - { - OPERATOR - }; - - enum FieldValues - { - Abs, - Floor, - Ceiling, - Sqrt, - Sin, - Cos, - Tan, - Asin, - Acos, - Atan, - Ln, - Log, - Eexp, - Op_10exp - }; - std::string name() const override; std::string description() const override; void registerBlocks(IEngine *engine) override; - static void compileAdd(Compiler *compiler); - static void compileSubtract(Compiler *compiler); - static void compileMultiply(Compiler *compiler); - static void compileDivide(Compiler *compiler); - static void compilePickRandom(Compiler *compiler); - static void compileLessThan(Compiler *compiler); - static void compileEquals(Compiler *compiler); - static void compileGreaterThan(Compiler *compiler); - static void compileAnd(Compiler *compiler); - static void compileOr(Compiler *compiler); - static void compileNot(Compiler *compiler); - static void compileJoin(Compiler *compiler); - static void compileLetterOf(Compiler *compiler); - static void compileLength(Compiler *compiler); - static void compileContains(Compiler *compiler); - static void compileMod(Compiler *compiler); - static void compileRound(Compiler *compiler); - static void compileMathOp(Compiler *compiler); - - static unsigned int op_ln(VirtualMachine *vm); - static unsigned int op_log(VirtualMachine *vm); - static unsigned int op_eexp(VirtualMachine *vm); - static unsigned int op_10exp(VirtualMachine *vm); + private: + static CompilerValue *compileAdd(Compiler *compiler); + static CompilerValue *compileSubtract(Compiler *compiler); + static CompilerValue *compileMultiply(Compiler *compiler); + static CompilerValue *compileDivide(Compiler *compiler); + static CompilerValue *compileRandom(Compiler *compiler); + static CompilerValue *compileLt(Compiler *compiler); + static CompilerValue *compileEquals(Compiler *compiler); + static CompilerValue *compileGt(Compiler *compiler); + static CompilerValue *compileAnd(Compiler *compiler); + static CompilerValue *compileOr(Compiler *compiler); + static CompilerValue *compileNot(Compiler *compiler); + static CompilerValue *compileJoin(Compiler *compiler); + static CompilerValue *compileLetterOf(Compiler *compiler); + static CompilerValue *compileLength(Compiler *compiler); + static CompilerValue *compileContains(Compiler *compiler); + static CompilerValue *compileMod(Compiler *compiler); + static CompilerValue *compileRound(Compiler *compiler); + static CompilerValue *compileMathOp(Compiler *compiler); }; } // namespace libscratchcpp diff --git a/src/blocks/sensingblocks.cpp b/src/blocks/sensingblocks.cpp index af1349ed..42896ecb 100644 --- a/src/blocks/sensingblocks.cpp +++ b/src/blocks/sensingblocks.cpp @@ -1,29 +1,9 @@ // SPDX-License-Identifier: Apache-2.0 -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include #include "sensingblocks.h" -#include "../engine/internal/clock.h" -#include "audio/audioinput.h" -#include "audio/iaudioloudness.h" - using namespace libscratchcpp; -IClock *SensingBlocks::clock = nullptr; -IAudioInput *SensingBlocks::audioInput = nullptr; - std::string SensingBlocks::name() const { return "Sensing"; @@ -36,1060 +16,4 @@ std::string SensingBlocks::description() const void SensingBlocks::registerBlocks(IEngine *engine) { - // Blocks - engine->addCompileFunction(this, "sensing_touchingobject", &compileTouchingObject); - engine->addCompileFunction(this, "sensing_touchingcolor", &compileTouchingColor); - engine->addCompileFunction(this, "sensing_coloristouchingcolor", &compileColorIsTouchingColor); - engine->addCompileFunction(this, "sensing_distanceto", &compileDistanceTo); - engine->addCompileFunction(this, "sensing_askandwait", &compileAskAndWait); - engine->addCompileFunction(this, "sensing_answer", &compileAnswer); - engine->addCompileFunction(this, "sensing_keypressed", &compileKeyPressed); - engine->addCompileFunction(this, "sensing_mousedown", &compileMouseDown); - engine->addCompileFunction(this, "sensing_mousex", &compileMouseX); - engine->addCompileFunction(this, "sensing_mousey", &compileMouseY); - engine->addCompileFunction(this, "sensing_setdragmode", &compileSetDragMode); - engine->addCompileFunction(this, "sensing_loudness", &compileLoudness); - engine->addCompileFunction(this, "sensing_loud", &compileLoud); - engine->addCompileFunction(this, "sensing_timer", &compileTimer); - engine->addCompileFunction(this, "sensing_resettimer", &compileResetTimer); - engine->addCompileFunction(this, "sensing_of", &compileOf); - engine->addCompileFunction(this, "sensing_current", &compileCurrent); - engine->addCompileFunction(this, "sensing_dayssince2000", &compileDaysSince2000); - - // Monitor names - engine->addMonitorNameFunction(this, "sensing_mousedown", &mouseDownMonitorName); - engine->addMonitorNameFunction(this, "sensing_mousex", &mouseXMonitorName); - engine->addMonitorNameFunction(this, "sensing_mousey", &mouseYMonitorName); - engine->addMonitorNameFunction(this, "sensing_loudness", &loudnessMonitorName); - engine->addMonitorNameFunction(this, "sensing_timer", &timerMonitorName); - engine->addMonitorNameFunction(this, "sensing_current", ¤tMonitorName); - engine->addMonitorNameFunction(this, "sensing_dayssince2000", &daysSince2000MonitorName); - - // Inputs - engine->addInput(this, "TOUCHINGOBJECTMENU", TOUCHINGOBJECTMENU); - engine->addInput(this, "COLOR", COLOR); - engine->addInput(this, "COLOR2", COLOR2); - engine->addInput(this, "DISTANCETOMENU", DISTANCETOMENU); - engine->addInput(this, "QUESTION", QUESTION); - engine->addInput(this, "KEY_OPTION", KEY_OPTION); - engine->addInput(this, "OBJECT", OBJECT); - - // Fields - engine->addField(this, "CURRENTMENU", CURRENTMENU); - engine->addField(this, "DRAG_MODE", DRAG_MODE); - engine->addField(this, "PROPERTY", PROPERTY); - - // Field values - engine->addFieldValue(this, "YEAR", YEAR); - engine->addFieldValue(this, "MONTH", MONTH); - engine->addFieldValue(this, "DATE", DATE); - engine->addFieldValue(this, "DAYOFWEEK", DAYOFWEEK); - engine->addFieldValue(this, "HOUR", HOUR); - engine->addFieldValue(this, "MINUTE", MINUTE); - engine->addFieldValue(this, "SECOND", SECOND); - engine->addFieldValue(this, "draggable", Draggable); - engine->addFieldValue(this, "not draggable", NotDraggable); - engine->addFieldValue(this, "x position", XPosition); - engine->addFieldValue(this, "y position", YPosition); - engine->addFieldValue(this, "direction", Direction); - engine->addFieldValue(this, "costume #", CostumeNumber); - engine->addFieldValue(this, "costume name", CostumeName); - engine->addFieldValue(this, "size", Size); - engine->addFieldValue(this, "volume", Volume); - engine->addFieldValue(this, "background #", BackdropNumber); // Scratch 1.4 support - engine->addFieldValue(this, "backdrop #", BackdropNumber); - engine->addFieldValue(this, "backdrop name", BackdropName); - - // Callbacks - engine->questionAnswered().connect(&onAnswer); -} - -void SensingBlocks::onInit(IEngine *engine) -{ - engine->threadAboutToStop().connect([engine](Thread *thread) { - if (!m_questionList.empty()) { - // Abort the question of this thread if it's currently being displayed - if (m_questionList.front()->vm == thread->vm()) { - thread->target()->bubble()->setText(""); - engine->questionAborted()(); - } - - m_questionList.erase( - std::remove_if(m_questionList.begin(), m_questionList.end(), [thread](const std::unique_ptr &question) { return question->vm == thread->vm(); }), - m_questionList.end()); - } - }); -} - -void SensingBlocks::compileTouchingObject(Compiler *compiler) -{ - Input *input = compiler->input(TOUCHINGOBJECTMENU); - - if (input->pointsToDropdownMenu()) { - std::string value = input->selectedMenuItem(); - - if (value == "_mouse_") - compiler->addFunctionCall(&touchingMousePointer); - else if (value == "_edge_") - compiler->addFunctionCall(&touchingEdge); - else { - int index = compiler->engine()->findTarget(value); - compiler->addConstValue(index); - compiler->addFunctionCall(&touchingObjectByIndex); - } - } else { - compiler->addInput(input); - compiler->addFunctionCall(&touchingObject); - } -} - -void SensingBlocks::compileTouchingColor(Compiler *compiler) -{ - compiler->addInput(COLOR); - compiler->addFunctionCall(&touchingColor); -} - -void SensingBlocks::compileColorIsTouchingColor(Compiler *compiler) -{ - compiler->addInput(COLOR2); // target color - compiler->addInput(COLOR); // mask color - compiler->addFunctionCall(&colorIsTouchingColor); -} - -void SensingBlocks::compileDistanceTo(Compiler *compiler) -{ - Input *input = compiler->input(DISTANCETOMENU); - - if (input->pointsToDropdownMenu()) { - std::string value = input->selectedMenuItem(); - - if (value == "_mouse_") - compiler->addFunctionCall(&distanceToMousePointer); - else { - int index = compiler->engine()->findTarget(value); - compiler->addConstValue(index); - compiler->addFunctionCall(&distanceToByIndex); - } - } else { - compiler->addInput(input); - compiler->addFunctionCall(&distanceTo); - } -} - -void SensingBlocks::compileAskAndWait(Compiler *compiler) -{ - compiler->addInput(QUESTION); - compiler->addFunctionCall(&askAndWait); -} - -void SensingBlocks::compileAnswer(Compiler *compiler) -{ - compiler->addFunctionCall(&answer); -} - -void SensingBlocks::compileKeyPressed(Compiler *compiler) -{ - compiler->addInput(KEY_OPTION); - compiler->addFunctionCall(&keyPressed); -} - -void SensingBlocks::compileMouseDown(Compiler *compiler) -{ - compiler->addFunctionCall(&mouseDown); -} - -void SensingBlocks::compileMouseX(Compiler *compiler) -{ - compiler->addFunctionCall(&mouseX); -} - -void SensingBlocks::compileMouseY(Compiler *compiler) -{ - compiler->addFunctionCall(&mouseY); -} - -void SensingBlocks::compileSetDragMode(Compiler *compiler) -{ - int option = compiler->field(DRAG_MODE)->specialValueId(); - - switch (option) { - case Draggable: - compiler->addFunctionCall(&setDraggableMode); - break; - - case NotDraggable: - compiler->addFunctionCall(&setNotDraggableMode); - break; - - default: - break; - } -} - -void SensingBlocks::compileLoudness(Compiler *compiler) -{ - compiler->addFunctionCall(&loudness); -} - -void SensingBlocks::compileLoud(Compiler *compiler) -{ - compiler->addFunctionCall(&loud); -} - -void SensingBlocks::compileTimer(Compiler *compiler) -{ - compiler->addFunctionCall(&timer); -} - -void SensingBlocks::compileResetTimer(Compiler *compiler) -{ - compiler->addFunctionCall(&resetTimer); -} - -void SensingBlocks::compileOf(Compiler *compiler) -{ - Field *property = compiler->field(PROPERTY); - assert(property); - int option = property->specialValueId(); - Input *input = compiler->input(OBJECT); - assert(input); - BlockFunc f = nullptr; - - if (input->pointsToDropdownMenu()) { - std::string value = input->selectedMenuItem(); - - IEngine *engine = compiler->engine(); - assert(engine); - int index = engine->findTarget(value); - - if (index == -1) { - compiler->addInstruction(vm::OP_NULL); - return; - } - - switch (option) { - case XPosition: - f = &xPositionOfSpriteByIndex; - break; - - case YPosition: - f = &yPositionOfSpriteByIndex; - break; - - case Direction: - f = &directionOfSpriteByIndex; - break; - - case CostumeNumber: - f = &costumeNumberOfSpriteByIndex; - break; - - case CostumeName: - f = &costumeNameOfSpriteByIndex; - break; - - case Size: - f = &sizeOfSpriteByIndex; - break; - - case Volume: - f = &volumeOfTargetByIndex; - break; - - case BackdropNumber: - f = &backdropNumberOfStageByIndex; - break; - - case BackdropName: - f = &backdropNameOfStageByIndex; - break; - - default: { - // Variable - f = &variableOfTargetByIndex; - Target *target = engine->targetAt(index); - auto varIndex = target->findVariable(property->value().toString()); - - if (varIndex == -1) - compiler->addInstruction(vm::OP_NULL); - else { - // NOTE: The OP_READ_VAR instruction can't be used for this (see #548) - compiler->addConstValue(varIndex); - } - - break; - } - } - - if (f) - compiler->addConstValue(index); - } else { - switch (option) { - case XPosition: - f = &xPositionOfSprite; - break; - - case YPosition: - f = &yPositionOfSprite; - break; - - case Direction: - f = &directionOfSprite; - break; - - case CostumeNumber: - f = &costumeNumberOfSprite; - break; - - case CostumeName: - f = &costumeNameOfSprite; - break; - - case Size: - f = &sizeOfSprite; - break; - - case Volume: - f = &volumeOfTarget; - break; - - case BackdropNumber: - f = &backdropNumberOfStage; - break; - - case BackdropName: - f = &backdropNameOfStage; - break; - - default: - f = &variableOfTarget; - compiler->addConstValue(property->value().toString()); - break; - } - - if (f) - compiler->addInput(input); - } - - if (f) - compiler->addFunctionCall(f); -} - -void SensingBlocks::compileCurrent(Compiler *compiler) -{ - int id = compiler->field(CURRENTMENU)->specialValueId(); - - switch (id) { - case YEAR: - compiler->addFunctionCall(¤tYear); - break; - - case MONTH: - compiler->addFunctionCall(¤tMonth); - break; - - case DATE: - compiler->addFunctionCall(¤tDate); - break; - - case DAYOFWEEK: - compiler->addFunctionCall(¤tDayOfWeek); - break; - - case HOUR: - compiler->addFunctionCall(¤tHour); - break; - - case MINUTE: - compiler->addFunctionCall(¤tMinute); - break; - - case SECOND: - compiler->addFunctionCall(¤tSecond); - break; - - default: - break; - } -} - -void SensingBlocks::compileDaysSince2000(Compiler *compiler) -{ - compiler->addFunctionCall(&daysSince2000); -} - -const std::string &SensingBlocks::mouseDownMonitorName(Block *block) -{ - static const std::string name = "mouse down?"; - return name; -} - -const std::string &SensingBlocks::mouseXMonitorName(Block *block) -{ - static const std::string name = "mouse x"; - return name; -} - -const std::string &SensingBlocks::mouseYMonitorName(Block *block) -{ - static const std::string name = "mouse y"; - return name; -} - -const std::string &SensingBlocks::loudnessMonitorName(Block *block) -{ - static const std::string name = "loudness"; - return name; -} - -const std::string &SensingBlocks::timerMonitorName(Block *block) -{ - static const std::string name = "timer"; - return name; -} - -const std::string &SensingBlocks::currentMonitorName(Block *block) -{ - int id = block->findFieldById(CURRENTMENU)->specialValueId(); - - switch (id) { - case YEAR: { - static const std::string name = "year"; - return name; - } - - case MONTH: { - static const std::string name = "month"; - return name; - } - - case DATE: { - static const std::string name = "date"; - return name; - } - - case DAYOFWEEK: { - static const std::string name = "day of week"; - return name; - } - - case HOUR: { - static const std::string name = "hour"; - return name; - } - - case MINUTE: { - static const std::string name = "minute"; - return name; - } - - case SECOND: { - static const std::string name = "second"; - return name; - } - - default: { - static const std::string name = ""; - return name; - } - } -} - -const std::string &SensingBlocks::daysSince2000MonitorName(Block *block) -{ - static const std::string name = "days since 2000"; - return name; -} - -unsigned int SensingBlocks::touchingObject(VirtualMachine *vm) -{ - std::string value = vm->getInput(0, 1)->toString(); - - if (value == "_mouse_") - vm->replaceReturnValue(vm->target()->touchingPoint(vm->engine()->mouseX(), vm->engine()->mouseY()), 1); - else if (value == "_edge_") - vm->replaceReturnValue(vm->target()->touchingEdge(), 1); - else { - Target *target = vm->engine()->targetAt(vm->engine()->findTarget(value)); - vm->replaceReturnValue(touchingObjectCommon(vm->target(), target), 1); - } - - return 0; -} - -unsigned int SensingBlocks::touchingObjectByIndex(VirtualMachine *vm) -{ - Target *target = vm->engine()->targetAt(vm->getInput(0, 1)->toInt()); - vm->replaceReturnValue(touchingObjectCommon(vm->target(), target), 1); - return 0; -} - -unsigned int SensingBlocks::touchingMousePointer(VirtualMachine *vm) -{ - vm->addReturnValue(vm->target()->touchingPoint(vm->engine()->mouseX(), vm->engine()->mouseY())); - return 0; -} - -unsigned int SensingBlocks::touchingEdge(VirtualMachine *vm) -{ - vm->addReturnValue(vm->target()->touchingEdge()); - return 0; -} - -unsigned int SensingBlocks::touchingColor(VirtualMachine *vm) -{ - vm->replaceReturnValue(vm->target()->touchingColor(vm->getInput(0, 1)->toRgba()), 1); - return 0; -} - -unsigned int SensingBlocks::colorIsTouchingColor(VirtualMachine *vm) -{ - vm->replaceReturnValue(vm->target()->touchingColor(vm->getInput(0, 2)->toRgba(), vm->getInput(1, 2)->toRgba()), 2); - return 1; -} - -unsigned int SensingBlocks::keyPressed(VirtualMachine *vm) -{ - vm->replaceReturnValue(vm->engine()->keyPressed(vm->getInput(0, 1)->toString()), 1); - return 0; -} - -unsigned int SensingBlocks::mouseDown(VirtualMachine *vm) -{ - vm->addReturnValue(vm->engine()->mousePressed()); - return 0; -} - -unsigned int SensingBlocks::mouseX(VirtualMachine *vm) -{ - vm->addReturnValue(vm->engine()->mouseX()); - return 0; -} - -unsigned int SensingBlocks::mouseY(VirtualMachine *vm) -{ - vm->addReturnValue(vm->engine()->mouseY()); - return 0; -} - -unsigned int SensingBlocks::setDraggableMode(VirtualMachine *vm) -{ - if (Sprite *sprite = dynamic_cast(vm->target())) - sprite->setDraggable(true); - - return 0; -} - -unsigned int SensingBlocks::setNotDraggableMode(VirtualMachine *vm) -{ - if (Sprite *sprite = dynamic_cast(vm->target())) - sprite->setDraggable(false); - - return 0; -} - -unsigned int SensingBlocks::loudness(VirtualMachine *vm) -{ - if (!audioInput) - audioInput = AudioInput::instance().get(); - - auto audioLoudness = audioInput->audioLoudness(); - vm->addReturnValue(audioLoudness->getLoudness()); - return 0; -} - -unsigned int SensingBlocks::loud(VirtualMachine *vm) -{ - if (!audioInput) - audioInput = AudioInput::instance().get(); - - auto audioLoudness = audioInput->audioLoudness(); - vm->addReturnValue(audioLoudness->getLoudness() > 10.0); - return 0; -} - -unsigned int SensingBlocks::distanceTo(VirtualMachine *vm) -{ - Sprite *sprite = dynamic_cast(vm->target()); - - if (!sprite) { - vm->replaceReturnValue(10000, 1); - return 0; - } - - std::string value = vm->getInput(0, 1)->toString(); - - if (value == "_mouse_") - vm->replaceReturnValue(std::sqrt(std::pow(sprite->x() - vm->engine()->mouseX(), 2) + std::pow(sprite->y() - vm->engine()->mouseY(), 2)), 1); - else { - Target *target = vm->engine()->targetAt(vm->engine()->findTarget(value)); - Sprite *targetSprite = dynamic_cast(target); - - if (targetSprite) - vm->replaceReturnValue(std::sqrt(std::pow(sprite->x() - targetSprite->x(), 2) + std::pow(sprite->y() - targetSprite->y(), 2)), 1); - else - vm->replaceReturnValue(10000, 1); - } - - return 0; -} - -unsigned int SensingBlocks::distanceToByIndex(VirtualMachine *vm) -{ - Sprite *sprite = dynamic_cast(vm->target()); - Target *target = vm->engine()->targetAt(vm->getInput(0, 1)->toInt()); - Sprite *targetSprite = dynamic_cast(target); - - if (sprite && targetSprite) - vm->replaceReturnValue(std::sqrt(std::pow(sprite->x() - targetSprite->x(), 2) + std::pow(sprite->y() - targetSprite->y(), 2)), 1); - else - vm->replaceReturnValue(10000, 1); - - return 0; -} - -unsigned int SensingBlocks::distanceToMousePointer(VirtualMachine *vm) -{ - Sprite *sprite = dynamic_cast(vm->target()); - - if (sprite) - vm->addReturnValue(std::sqrt(std::pow(sprite->x() - vm->engine()->mouseX(), 2) + std::pow(sprite->y() - vm->engine()->mouseY(), 2))); - else - vm->addReturnValue(10000); - - return 0; -} - -void SensingBlocks::onAnswer(const std::string &answer) -{ - // https://github.com/scratchfoundation/scratch-vm/blob/6055823f203a696165084b873e661713806583ec/src/blocks/scratch3_sensing.js#L99-L115 - m_answer = answer; - - if (!m_questionList.empty()) { - Question *question = m_questionList.front().get(); - VirtualMachine *vm = question->vm; - assert(vm); - assert(vm->target()); - - // If the target was visible when asked, hide the say bubble unless the target was the stage - if (question->wasVisible && !question->wasStage) - vm->target()->bubble()->setText(""); - - m_questionList.erase(m_questionList.begin()); - vm->resolvePromise(); - askNextQuestion(); - } -} - -unsigned int SensingBlocks::askAndWait(VirtualMachine *vm) -{ - const bool isQuestionAsked = !m_questionList.empty(); - enqueueAsk(vm->getInput(0, 1)->toString(), vm); - - if (!isQuestionAsked) - askNextQuestion(); - - vm->promise(); - return 1; -} - -unsigned int SensingBlocks::answer(VirtualMachine *vm) -{ - vm->addReturnValue(m_answer); - return 0; -} - -unsigned int SensingBlocks::timer(VirtualMachine *vm) -{ - vm->addReturnValue(vm->engine()->timer()->value()); - return 0; -} - -unsigned int SensingBlocks::resetTimer(VirtualMachine *vm) -{ - vm->engine()->timer()->reset(); - return 0; -} - -unsigned int SensingBlocks::xPositionOfSprite(VirtualMachine *vm) -{ - Target *target = vm->engine()->targetAt(vm->engine()->findTarget(vm->getInput(0, 1)->toString())); - Sprite *sprite = dynamic_cast(target); - - if (sprite) - vm->replaceReturnValue(sprite->x(), 1); - else - vm->replaceReturnValue(0, 1); - - return 0; -} - -unsigned int SensingBlocks::xPositionOfSpriteByIndex(VirtualMachine *vm) -{ - Target *target = vm->engine()->targetAt(vm->getInput(0, 1)->toInt()); - Sprite *sprite = dynamic_cast(target); - - if (sprite) - vm->replaceReturnValue(sprite->x(), 1); - else - vm->replaceReturnValue(0, 1); - - return 0; -} - -unsigned int SensingBlocks::yPositionOfSprite(VirtualMachine *vm) -{ - Target *target = vm->engine()->targetAt(vm->engine()->findTarget(vm->getInput(0, 1)->toString())); - Sprite *sprite = dynamic_cast(target); - - if (sprite) - vm->replaceReturnValue(sprite->y(), 1); - else - vm->replaceReturnValue(0, 1); - - return 0; -} - -unsigned int SensingBlocks::yPositionOfSpriteByIndex(VirtualMachine *vm) -{ - Target *target = vm->engine()->targetAt(vm->getInput(0, 1)->toInt()); - Sprite *sprite = dynamic_cast(target); - - if (sprite) - vm->replaceReturnValue(sprite->y(), 1); - else - vm->replaceReturnValue(0, 1); - - return 0; -} - -unsigned int SensingBlocks::directionOfSprite(VirtualMachine *vm) -{ - Target *target = vm->engine()->targetAt(vm->engine()->findTarget(vm->getInput(0, 1)->toString())); - Sprite *sprite = dynamic_cast(target); - - if (sprite) - vm->replaceReturnValue(sprite->direction(), 1); - else - vm->replaceReturnValue(0, 1); - - return 0; -} - -unsigned int SensingBlocks::directionOfSpriteByIndex(VirtualMachine *vm) -{ - Target *target = vm->engine()->targetAt(vm->getInput(0, 1)->toInt()); - Sprite *sprite = dynamic_cast(target); - - if (sprite) - vm->replaceReturnValue(sprite->direction(), 1); - else - vm->replaceReturnValue(0, 1); - - return 0; -} - -unsigned int SensingBlocks::costumeNumberOfSprite(VirtualMachine *vm) -{ - Target *target = vm->engine()->targetAt(vm->engine()->findTarget(vm->getInput(0, 1)->toString())); - Sprite *sprite = dynamic_cast(target); - - if (sprite) - vm->replaceReturnValue(sprite->costumeIndex() + 1, 1); - else - vm->replaceReturnValue(0, 1); - - return 0; -} - -unsigned int SensingBlocks::costumeNumberOfSpriteByIndex(VirtualMachine *vm) -{ - Target *target = vm->engine()->targetAt(vm->getInput(0, 1)->toInt()); - Sprite *sprite = dynamic_cast(target); - - if (sprite) - vm->replaceReturnValue(sprite->costumeIndex() + 1, 1); - else - vm->replaceReturnValue(0, 1); - - return 0; -} - -unsigned int SensingBlocks::costumeNameOfSprite(VirtualMachine *vm) -{ - Target *target = vm->engine()->targetAt(vm->engine()->findTarget(vm->getInput(0, 1)->toString())); - Sprite *sprite = dynamic_cast(target); - - if (sprite) - vm->replaceReturnValue(sprite->currentCostume()->name(), 1); - else - vm->replaceReturnValue(0, 1); - - return 0; -} - -unsigned int SensingBlocks::costumeNameOfSpriteByIndex(VirtualMachine *vm) -{ - Target *target = vm->engine()->targetAt(vm->getInput(0, 1)->toInt()); - Sprite *sprite = dynamic_cast(target); - - if (sprite) - vm->replaceReturnValue(sprite->currentCostume()->name(), 1); - else - vm->replaceReturnValue(0, 1); - - return 0; -} - -unsigned int SensingBlocks::sizeOfSprite(VirtualMachine *vm) -{ - Target *target = vm->engine()->targetAt(vm->engine()->findTarget(vm->getInput(0, 1)->toString())); - Sprite *sprite = dynamic_cast(target); - - if (sprite) - vm->replaceReturnValue(sprite->size(), 1); - else - vm->replaceReturnValue(0, 1); - - return 0; -} - -unsigned int SensingBlocks::sizeOfSpriteByIndex(VirtualMachine *vm) -{ - Target *target = vm->engine()->targetAt(vm->getInput(0, 1)->toInt()); - Sprite *sprite = dynamic_cast(target); - - if (sprite) - vm->replaceReturnValue(sprite->size(), 1); - else - vm->replaceReturnValue(0, 1); - - return 0; -} - -unsigned int SensingBlocks::volumeOfTarget(VirtualMachine *vm) -{ - Target *target = vm->engine()->targetAt(vm->engine()->findTarget(vm->getInput(0, 1)->toString())); - - if (target) - vm->replaceReturnValue(target->volume(), 1); - else - vm->replaceReturnValue(0, 1); - - return 0; -} - -unsigned int SensingBlocks::volumeOfTargetByIndex(VirtualMachine *vm) -{ - Target *target = vm->engine()->targetAt(vm->getInput(0, 1)->toInt()); - - if (target) - vm->replaceReturnValue(target->volume(), 1); - else - vm->replaceReturnValue(0, 1); - - return 0; -} - -unsigned int SensingBlocks::variableOfTarget(VirtualMachine *vm) -{ - Target *target = vm->engine()->targetAt(vm->engine()->findTarget(vm->getInput(1, 2)->toString())); - - if (target) { - auto varIndex = target->findVariable(vm->getInput(0, 2)->toString()); - - if (varIndex == -1) - vm->replaceReturnValue(0, 2); - else - vm->replaceReturnValue(target->variableAt(varIndex)->value(), 2); - } else - vm->replaceReturnValue(0, 2); - - return 1; -} - -unsigned int SensingBlocks::variableOfTargetByIndex(VirtualMachine *vm) -{ - Target *target = vm->engine()->targetAt(vm->getInput(0, 1)->toInt()); - - if (target) { - const int varIndex = vm->getInput(0, 2)->toInt(); - vm->replaceReturnValue(target->variableAt(varIndex)->value(), 2); - } else - vm->replaceReturnValue(0, 2); - - return 1; -} - -unsigned int SensingBlocks::backdropNumberOfStage(VirtualMachine *vm) -{ - Target *target = vm->engine()->targetAt(vm->engine()->findTarget(vm->getInput(0, 1)->toString())); - Stage *stage = dynamic_cast(target); - - if (stage) - vm->replaceReturnValue(stage->costumeIndex() + 1, 1); - else - vm->replaceReturnValue(0, 1); - - return 0; -} - -unsigned int SensingBlocks::backdropNumberOfStageByIndex(VirtualMachine *vm) -{ - Target *target = vm->engine()->targetAt(vm->getInput(0, 1)->toInt()); - Stage *stage = dynamic_cast(target); - - if (stage) - vm->replaceReturnValue(stage->costumeIndex() + 1, 1); - else - vm->replaceReturnValue(0, 1); - - return 0; -} - -unsigned int SensingBlocks::backdropNameOfStage(VirtualMachine *vm) -{ - Target *target = vm->engine()->targetAt(vm->engine()->findTarget(vm->getInput(0, 1)->toString())); - Stage *stage = dynamic_cast(target); - - if (stage) - vm->replaceReturnValue(stage->currentCostume()->name(), 1); - else - vm->replaceReturnValue(0, 1); - - return 0; -} - -unsigned int SensingBlocks::backdropNameOfStageByIndex(VirtualMachine *vm) -{ - Target *target = vm->engine()->targetAt(vm->getInput(0, 1)->toInt()); - Stage *stage = dynamic_cast(target); - - if (stage) - vm->replaceReturnValue(stage->currentCostume()->name(), 1); - else - vm->replaceReturnValue(0, 1); - - return 0; -} - -unsigned int SensingBlocks::currentYear(VirtualMachine *vm) -{ - time_t now = time(0); - tm *ltm = localtime(&now); - vm->addReturnValue(ltm->tm_year + 1900); - - return 0; -} - -unsigned int SensingBlocks::currentMonth(VirtualMachine *vm) -{ - time_t now = time(0); - tm *ltm = localtime(&now); - vm->addReturnValue(ltm->tm_mon + 1); - - return 0; -} - -unsigned int SensingBlocks::currentDate(VirtualMachine *vm) -{ - time_t now = time(0); - tm *ltm = localtime(&now); - vm->addReturnValue(ltm->tm_mday); - - return 0; -} - -unsigned int SensingBlocks::currentDayOfWeek(VirtualMachine *vm) -{ - time_t now = time(0); - tm *ltm = localtime(&now); - vm->addReturnValue(ltm->tm_wday + 1); - - return 0; -} - -unsigned int SensingBlocks::currentHour(VirtualMachine *vm) -{ - time_t now = time(0); - tm *ltm = localtime(&now); - vm->addReturnValue(ltm->tm_hour); - - return 0; -} - -unsigned int SensingBlocks::currentMinute(VirtualMachine *vm) -{ - time_t now = time(0); - tm *ltm = localtime(&now); - vm->addReturnValue(ltm->tm_min); - - return 0; -} - -unsigned int SensingBlocks::currentSecond(VirtualMachine *vm) -{ - time_t now = time(0); - tm *ltm = localtime(&now); - vm->addReturnValue(ltm->tm_sec); - - return 0; -} - -unsigned int SensingBlocks::daysSince2000(VirtualMachine *vm) -{ - if (!clock) - clock = Clock::instance().get(); - - auto ms = std::chrono::duration_cast(clock->currentSystemTime().time_since_epoch()).count(); - vm->addReturnValue(ms / 86400000.0 - 10957); - - return 0; -} - -bool SensingBlocks::touchingObjectCommon(Target *source, Target *target) -{ - if (source && target && !target->isStage()) - return source->touchingSprite(static_cast(target)); - - return false; -} - -void SensingBlocks::enqueueAsk(const std::string &question, VirtualMachine *vm) -{ - // https://github.com/scratchfoundation/scratch-vm/blob/6055823f203a696165084b873e661713806583ec/src/blocks/scratch3_sensing.js#L117-L119 - assert(vm); - Target *target = vm->target(); - assert(target); - bool visible = true; - bool isStage = target->isStage(); - - if (!isStage) { - Sprite *sprite = static_cast(target); - visible = sprite->visible(); - } - - m_questionList.push_back(std::make_unique(question, vm, visible, isStage)); -} - -void SensingBlocks::askNextQuestion() -{ - // https://github.com/scratchfoundation/scratch-vm/blob/6055823f203a696165084b873e661713806583ec/src/blocks/scratch3_sensing.js#L121-L133 - if (m_questionList.empty()) - return; - - Question *question = m_questionList.front().get(); - Target *target = question->vm->target(); - IEngine *engine = question->vm->engine(); - - // If the target is visible, emit a blank question and show - // a bubble unless the target was the stage - if (question->wasVisible && !question->wasStage) { - target->bubble()->setType(TextBubble::Type::Say); - target->bubble()->setText(question->question); - - engine->questionAsked()(""); - } else { - engine->questionAsked()(question->question); - } } diff --git a/src/blocks/sensingblocks.h b/src/blocks/sensingblocks.h index 8153a18b..ab934fb0 100644 --- a/src/blocks/sensingblocks.h +++ b/src/blocks/sensingblocks.h @@ -3,182 +3,17 @@ #pragma once #include -#include -#include - -#include "../engine/internal/clock.h" namespace libscratchcpp { -class Target; -class IAudioInput; - -/*! \brief The SensingBlocks class contains the implementation of sensing blocks. */ class SensingBlocks : public IExtension { public: - enum Inputs - { - TOUCHINGOBJECTMENU, - COLOR, - COLOR2, - DISTANCETOMENU, - QUESTION, - KEY_OPTION, - OBJECT - }; - - enum Fields - { - CURRENTMENU, - DRAG_MODE, - PROPERTY - }; - - enum FieldValues - { - YEAR, - MONTH, - DATE, - DAYOFWEEK, - HOUR, - MINUTE, - SECOND, - Draggable, - NotDraggable, - XPosition, - YPosition, - Direction, - CostumeNumber, - CostumeName, - Size, - Volume, - BackdropNumber, - BackdropName - }; - std::string name() const override; std::string description() const override; void registerBlocks(IEngine *engine) override; - void onInit(IEngine *engine) override; - - static void compileTouchingObject(Compiler *compiler); - static void compileTouchingColor(Compiler *compiler); - static void compileColorIsTouchingColor(Compiler *compiler); - static void compileDistanceTo(Compiler *compiler); - static void compileAskAndWait(Compiler *compiler); - static void compileAnswer(Compiler *compiler); - static void compileKeyPressed(Compiler *compiler); - static void compileMouseDown(Compiler *compiler); - static void compileMouseX(Compiler *compiler); - static void compileMouseY(Compiler *compiler); - static void compileSetDragMode(Compiler *compiler); - static void compileLoudness(Compiler *compiler); - static void compileLoud(Compiler *compiler); - static void compileTimer(Compiler *compiler); - static void compileResetTimer(Compiler *compiler); - static void compileOf(Compiler *compiler); - static void compileCurrent(Compiler *compiler); - static void compileDaysSince2000(Compiler *compiler); - - static const std::string &mouseDownMonitorName(Block *block); - static const std::string &mouseXMonitorName(Block *block); - static const std::string &mouseYMonitorName(Block *block); - static const std::string &loudnessMonitorName(Block *block); - static const std::string &timerMonitorName(Block *block); - static const std::string ¤tMonitorName(Block *block); - static const std::string &daysSince2000MonitorName(Block *block); - - static unsigned int touchingObject(VirtualMachine *vm); - static unsigned int touchingObjectByIndex(VirtualMachine *vm); - static unsigned int touchingMousePointer(VirtualMachine *vm); - static unsigned int touchingEdge(VirtualMachine *vm); - - static unsigned int touchingColor(VirtualMachine *vm); - - static unsigned int colorIsTouchingColor(VirtualMachine *vm); - - static unsigned int keyPressed(VirtualMachine *vm); - static unsigned int mouseDown(VirtualMachine *vm); - static unsigned int mouseX(VirtualMachine *vm); - static unsigned int mouseY(VirtualMachine *vm); - - static unsigned int setDraggableMode(VirtualMachine *vm); - static unsigned int setNotDraggableMode(VirtualMachine *vm); - - static unsigned int loudness(VirtualMachine *vm); - static unsigned int loud(VirtualMachine *vm); - - static unsigned int distanceTo(VirtualMachine *vm); - static unsigned int distanceToByIndex(VirtualMachine *vm); - static unsigned int distanceToMousePointer(VirtualMachine *vm); - - static void onAnswer(const std::string &answer); - static unsigned int askAndWait(VirtualMachine *vm); - static unsigned int answer(VirtualMachine *vm); - - static unsigned int timer(VirtualMachine *vm); - static unsigned int resetTimer(VirtualMachine *vm); - - static unsigned int xPositionOfSprite(VirtualMachine *vm); - static unsigned int xPositionOfSpriteByIndex(VirtualMachine *vm); - static unsigned int yPositionOfSprite(VirtualMachine *vm); - static unsigned int yPositionOfSpriteByIndex(VirtualMachine *vm); - static unsigned int directionOfSprite(VirtualMachine *vm); - static unsigned int directionOfSpriteByIndex(VirtualMachine *vm); - static unsigned int costumeNumberOfSprite(VirtualMachine *vm); - static unsigned int costumeNumberOfSpriteByIndex(VirtualMachine *vm); - static unsigned int costumeNameOfSprite(VirtualMachine *vm); - static unsigned int costumeNameOfSpriteByIndex(VirtualMachine *vm); - static unsigned int sizeOfSprite(VirtualMachine *vm); - static unsigned int sizeOfSpriteByIndex(VirtualMachine *vm); - static unsigned int volumeOfTarget(VirtualMachine *vm); - static unsigned int volumeOfTargetByIndex(VirtualMachine *vm); - static unsigned int variableOfTarget(VirtualMachine *vm); - static unsigned int variableOfTargetByIndex(VirtualMachine *vm); - static unsigned int backdropNumberOfStage(VirtualMachine *vm); - static unsigned int backdropNumberOfStageByIndex(VirtualMachine *vm); - static unsigned int backdropNameOfStage(VirtualMachine *vm); - static unsigned int backdropNameOfStageByIndex(VirtualMachine *vm); - - static unsigned int currentYear(VirtualMachine *vm); - static unsigned int currentMonth(VirtualMachine *vm); - static unsigned int currentDate(VirtualMachine *vm); - static unsigned int currentDayOfWeek(VirtualMachine *vm); - static unsigned int currentHour(VirtualMachine *vm); - static unsigned int currentMinute(VirtualMachine *vm); - static unsigned int currentSecond(VirtualMachine *vm); - static unsigned int daysSince2000(VirtualMachine *vm); - - static IClock *clock; - static IAudioInput *audioInput; - - private: - struct Question - { - Question(const std::string &question, VirtualMachine *vm, bool wasVisible, bool wasStage) : - question(question), - vm(vm), - wasVisible(wasVisible), - wasStage(wasStage) - { - } - - std::string question; - VirtualMachine *vm = nullptr; - bool wasVisible = false; - bool wasStage = false; - }; - - static bool touchingObjectCommon(Target *source, Target *target); - - static void enqueueAsk(const std::string &question, VirtualMachine *vm); - static void askNextQuestion(); - - static inline std::vector> m_questionList; - static inline Value m_answer; }; } // namespace libscratchcpp diff --git a/src/blocks/soundblocks.cpp b/src/blocks/soundblocks.cpp index 6cc2aa7b..4a440a9a 100644 --- a/src/blocks/soundblocks.cpp +++ b/src/blocks/soundblocks.cpp @@ -1,29 +1,9 @@ // SPDX-License-Identifier: Apache-2.0 -#include -#include -#include -#include -#include -#include -#include - #include "soundblocks.h" using namespace libscratchcpp; -// TODO: Use C++20 -template -static void erase_if(ContainerT &items, const PredicateT &predicate) -{ - for (auto it = items.begin(); it != items.end();) { - if (predicate(*it)) - it = items.erase(it); - else - ++it; - } -} - std::string SoundBlocks::name() const { return "Sound"; @@ -36,391 +16,4 @@ std::string SoundBlocks::description() const void SoundBlocks::registerBlocks(IEngine *engine) { - // Blocks - engine->addCompileFunction(this, "sound_play", &compilePlay); - engine->addCompileFunction(this, "sound_playuntildone", &compilePlayUntilDone); - engine->addCompileFunction(this, "sound_stopallsounds", &compileStopAllSounds); - engine->addCompileFunction(this, "sound_seteffectto", &compileSetEffectTo); - engine->addCompileFunction(this, "sound_changeeffectby", &compileChangeEffectBy); - engine->addCompileFunction(this, "sound_changevolumeby", &compileChangeVolumeBy); - engine->addCompileFunction(this, "sound_cleareffects", &compileClearEffects); - engine->addCompileFunction(this, "sound_setvolumeto", &compileSetVolumeTo); - engine->addCompileFunction(this, "sound_volume", &compileVolume); - - // Monitor names - engine->addMonitorNameFunction(this, "sound_volume", &volumeMonitorName); - - // Inputs - engine->addInput(this, "SOUND_MENU", SOUND_MENU); - engine->addInput(this, "VALUE", VALUE); - engine->addInput(this, "VOLUME", VOLUME); - - // Fields - engine->addField(this, "EFFECT", EFFECT); - - // Field values - engine->addFieldValue(this, "PITCH", PITCH); - engine->addFieldValue(this, "PAN", PAN); -} - -void SoundBlocks::onInit(IEngine *engine) -{ - m_waitingSounds.clear(); - engine->threadAboutToStop().connect([](Thread *thread) { erase_if(m_waitingSounds, [thread](const std::pair &pair) { return pair.second == thread->vm(); }); }); -} - -bool SoundBlocks::compilePlayCommon(Compiler *compiler, bool untilDone, bool *byIndex) -{ - Target *target = compiler->target(); - assert(target); - - if (!target) - return false; - - Input *input = compiler->input(SOUND_MENU); - - if (input->pointsToDropdownMenu()) { - std::string value = input->selectedMenuItem(); - - int index = target->findSound(value); - - if (index == -1) { - Value v(value); - - if (v.isValidNumber() && !v.isInfinity() && !v.isNegativeInfinity() && !(v.isString() && v.toString().empty())) { - compiler->addConstValue(v.toLong() - 1); - compiler->addFunctionCall(untilDone ? &playByIndexUntilDone : &playByIndex); - - if (byIndex) - *byIndex = true; - - return true; - } - } else { - compiler->addConstValue(index); - compiler->addFunctionCall(untilDone ? &playByIndexUntilDone : &playByIndex); - - if (byIndex) - *byIndex = true; - - return true; - } - } else { - compiler->addInput(input); - compiler->addFunctionCall(untilDone ? &playUntilDone : &play); - - if (byIndex) - *byIndex = false; - - return true; - } - - return false; -} - -void SoundBlocks::compilePlay(Compiler *compiler) -{ - compilePlayCommon(compiler, false); -} - -void SoundBlocks::compilePlayUntilDone(Compiler *compiler) -{ - bool byIndex = false; - - if (compilePlayCommon(compiler, true, &byIndex)) - compiler->addFunctionCall(byIndex ? &checkSoundByIndex : &checkSound); -} - -void SoundBlocks::compileStopAllSounds(Compiler *compiler) -{ - compiler->addFunctionCall(&stopAllSounds); -} - -void SoundBlocks::compileSetEffectTo(Compiler *compiler) -{ - compiler->addInput(VALUE); - int option = compiler->field(EFFECT)->specialValueId(); - - switch (option) { - case PITCH: - compiler->addFunctionCall(&setPitchEffectTo); - break; - - case PAN: - compiler->addFunctionCall(&setPanEffectTo); - break; - - default: - assert(false); - break; - } -} - -void SoundBlocks::compileChangeEffectBy(Compiler *compiler) -{ - compiler->addInput(VALUE); - int option = compiler->field(EFFECT)->specialValueId(); - - switch (option) { - case PITCH: - compiler->addFunctionCall(&changePitchEffectBy); - break; - - case PAN: - compiler->addFunctionCall(&changePanEffectBy); - break; - - default: - assert(false); - break; - } -} - -void SoundBlocks::compileClearEffects(Compiler *compiler) -{ - compiler->addFunctionCall(&clearEffects); -} - -void SoundBlocks::compileChangeVolumeBy(Compiler *compiler) -{ - compiler->addInput(VOLUME); - compiler->addFunctionCall(&changeVolumeBy); -} - -void SoundBlocks::compileSetVolumeTo(Compiler *compiler) -{ - compiler->addInput(VOLUME); - compiler->addFunctionCall(&setVolumeTo); -} - -void SoundBlocks::compileVolume(Compiler *compiler) -{ - compiler->addFunctionCall(&volume); -} - -const std::string &SoundBlocks::volumeMonitorName(Block *block) -{ - static const std::string name = "volume"; - return name; -} - -Sound *SoundBlocks::getSoundByIndex(Target *target, long index) -{ - long soundCount = target->sounds().size(); - - if (index < 0 || index >= soundCount) { - if (index < 0) - index = std::fmod(soundCount + std::fmod(index, -soundCount), soundCount); - else - index = std::fmod(index, soundCount); - } - - return target->soundAt(index).get(); -} - -Sound *SoundBlocks::playCommon(VirtualMachine *vm) -{ - Target *target = vm->target(); - assert(target); - const Value *name = vm->getInput(0, 1); - - if (target) { - Sound *sound = target->soundAt(target->findSound(name->toString())).get(); - - if (sound) { - sound->start(); - return sound; - } - - if (name->isValidNumber() && !name->isInfinity() && !name->isNegativeInfinity() && !(name->isString() && name->toString().empty())) { - sound = getSoundByIndex(target, name->toLong() - 1); - - if (sound) { - sound->start(); - return sound; - } - } - } - - return nullptr; -} - -Sound *SoundBlocks::playByIndexCommon(VirtualMachine *vm) -{ - Target *target = vm->target(); - assert(target); - - if (target) { - Sound *sound = getSoundByIndex(target, vm->getInput(0, 1)->toInt()); - - if (sound) { - sound->start(); - return sound; - } - } - - return nullptr; -} - -unsigned int SoundBlocks::play(VirtualMachine *vm) -{ - Sound *sound = playCommon(vm); - - if (sound) - m_waitingSounds.erase(sound); - - return 1; -} - -unsigned int SoundBlocks::playByIndex(VirtualMachine *vm) -{ - Sound *sound = playByIndexCommon(vm); - - if (sound) - m_waitingSounds.erase(sound); - - return 1; -} - -unsigned int SoundBlocks::playUntilDone(VirtualMachine *vm) -{ - Sound *sound = playCommon(vm); - - if (sound) - m_waitingSounds[sound] = vm; - - return 0; // leave the register for checkSound() -} - -unsigned int SoundBlocks::playByIndexUntilDone(VirtualMachine *vm) -{ - Sound *sound = playByIndexCommon(vm); - - if (sound) - m_waitingSounds[sound] = vm; - - return 0; // leave the register for checkSoundByIndex() -} - -unsigned int SoundBlocks::checkSound(VirtualMachine *vm) -{ - Target *target = vm->target(); - assert(target); - const Value *name = vm->getInput(0, 1); - - if (target) { - Sound *sound = target->soundAt(target->findSound(name->toString())).get(); - - if (!sound && name->isValidNumber() && !name->isInfinity() && !name->isNegativeInfinity() && !(name->isString() && name->toString().empty())) - sound = getSoundByIndex(target, name->toLong() - 1); - - if (sound) { - auto it = m_waitingSounds.find(sound); - - if (it != m_waitingSounds.cend() && it->second == vm) { - if (sound->isPlaying()) - vm->stop(true, true, true); - else - m_waitingSounds.erase(sound); - } - } - } - - return 1; -} - -unsigned int SoundBlocks::checkSoundByIndex(VirtualMachine *vm) -{ - Target *target = vm->target(); - assert(target); - - if (target) { - auto sound = getSoundByIndex(target, vm->getInput(0, 1)->toInt()); - - if (sound) { - auto it = m_waitingSounds.find(sound); - - if (it != m_waitingSounds.cend() && it->second == vm) { - if (sound->isPlaying()) - vm->stop(true, true, true); - else - m_waitingSounds.erase(sound); - } - } - } - - return 1; -} - -unsigned int SoundBlocks::stopAllSounds(VirtualMachine *vm) -{ - vm->engine()->stopSounds(); - m_waitingSounds.clear(); - return 0; -} - -unsigned int SoundBlocks::setPitchEffectTo(VirtualMachine *vm) -{ - if (Target *target = vm->target()) - target->setSoundEffectValue(Sound::Effect::Pitch, vm->getInput(0, 1)->toDouble()); - - return 1; -} - -unsigned int SoundBlocks::setPanEffectTo(VirtualMachine *vm) -{ - if (Target *target = vm->target()) - target->setSoundEffectValue(Sound::Effect::Pan, vm->getInput(0, 1)->toDouble()); - - return 1; -} - -unsigned int SoundBlocks::changePitchEffectBy(VirtualMachine *vm) -{ - if (Target *target = vm->target()) - target->setSoundEffectValue(Sound::Effect::Pitch, target->soundEffectValue(Sound::Effect::Pitch) + vm->getInput(0, 1)->toDouble()); - - return 1; -} - -unsigned int SoundBlocks::changePanEffectBy(VirtualMachine *vm) -{ - if (Target *target = vm->target()) - target->setSoundEffectValue(Sound::Effect::Pan, target->soundEffectValue(Sound::Effect::Pan) + vm->getInput(0, 1)->toDouble()); - - return 1; -} - -unsigned int SoundBlocks::clearEffects(VirtualMachine *vm) -{ - if (Target *target = vm->target()) - target->clearSoundEffects(); - - return 0; -} - -unsigned int SoundBlocks::changeVolumeBy(VirtualMachine *vm) -{ - if (Target *target = vm->target()) - target->setVolume(target->volume() + vm->getInput(0, 1)->toDouble()); - - return 1; -} - -unsigned int SoundBlocks::setVolumeTo(VirtualMachine *vm) -{ - if (Target *target = vm->target()) - target->setVolume(vm->getInput(0, 1)->toDouble()); - - return 1; -} - -unsigned int SoundBlocks::volume(VirtualMachine *vm) -{ - if (Target *target = vm->target()) - vm->addReturnValue(target->volume()); - else - vm->addReturnValue(0); - - return 0; } diff --git a/src/blocks/soundblocks.h b/src/blocks/soundblocks.h index d31b8a62..afae6c9c 100644 --- a/src/blocks/soundblocks.h +++ b/src/blocks/soundblocks.h @@ -2,83 +2,18 @@ #pragma once -#include -#include - #include namespace libscratchcpp { -class Sound; -class Target; - -/*! \brief The SoundBlocks class contains the implementation of sound blocks. */ class SoundBlocks : public IExtension { public: - enum Inputs - { - SOUND_MENU, - VALUE, - VOLUME - }; - - enum Fields - { - EFFECT - }; - - enum FieldValues - { - PITCH, - PAN - }; - std::string name() const override; std::string description() const override; void registerBlocks(IEngine *engine) override; - void onInit(IEngine *engine) override; - - static bool compilePlayCommon(Compiler *compiler, bool untilDone, bool *byIndex = nullptr); - static void compilePlay(Compiler *compiler); - static void compilePlayUntilDone(Compiler *compiler); - static void compileStopAllSounds(Compiler *compiler); - static void compileSetEffectTo(Compiler *compiler); - static void compileChangeEffectBy(Compiler *compiler); - static void compileClearEffects(Compiler *compiler); - static void compileChangeVolumeBy(Compiler *compiler); - static void compileSetVolumeTo(Compiler *compiler); - static void compileVolume(Compiler *compiler); - - static const std::string &volumeMonitorName(Block *block); - - static Sound *getSoundByIndex(Target *target, long index); - static Sound *playCommon(VirtualMachine *vm); - static Sound *playByIndexCommon(VirtualMachine *vm); - - static unsigned int play(VirtualMachine *vm); - static unsigned int playByIndex(VirtualMachine *vm); - - static unsigned int playUntilDone(VirtualMachine *vm); - static unsigned int playByIndexUntilDone(VirtualMachine *vm); - static unsigned int checkSound(VirtualMachine *vm); - static unsigned int checkSoundByIndex(VirtualMachine *vm); - - static unsigned int stopAllSounds(VirtualMachine *vm); - - static unsigned int setPitchEffectTo(VirtualMachine *vm); - static unsigned int setPanEffectTo(VirtualMachine *vm); - static unsigned int changePitchEffectBy(VirtualMachine *vm); - static unsigned int changePanEffectBy(VirtualMachine *vm); - static unsigned int clearEffects(VirtualMachine *vm); - - static unsigned int changeVolumeBy(VirtualMachine *vm); - static unsigned int setVolumeTo(VirtualMachine *vm); - static unsigned int volume(VirtualMachine *vm); - - static inline std::unordered_map m_waitingSounds; }; } // namespace libscratchcpp diff --git a/src/blocks/variableblocks.cpp b/src/blocks/variableblocks.cpp index 81961e49..2e845a68 100644 --- a/src/blocks/variableblocks.cpp +++ b/src/blocks/variableblocks.cpp @@ -2,12 +2,10 @@ #include #include +#include +#include #include -#include #include -#include -#include -#include #include "variableblocks.h" @@ -25,154 +23,45 @@ std::string VariableBlocks::description() const void VariableBlocks::registerBlocks(IEngine *engine) { - // Blocks engine->addCompileFunction(this, "data_variable", &compileVariable); - engine->addCompileFunction(this, "data_setvariableto", &compileSetVariable); + engine->addCompileFunction(this, "data_setvariableto", &compileSetVariableTo); engine->addCompileFunction(this, "data_changevariableby", &compileChangeVariableBy); - engine->addCompileFunction(this, "data_showvariable", &compileShowVariable); - engine->addCompileFunction(this, "data_hidevariable", &compileHideVariable); - - // Monitor names - engine->addMonitorNameFunction(this, "data_variable", &variableMonitorName); - - // Monitor change functions - engine->addMonitorChangeFunction(this, "data_variable", &changeVariableMonitorValue); - - // Inputs - engine->addInput(this, "VALUE", VALUE); - - // Fields - engine->addField(this, "VARIABLE", VARIABLE); -} - -void VariableBlocks::compileVariable(Compiler *compiler) -{ - // NOTE: This block is only used by variable monitors - compiler->addInstruction(vm::OP_READ_VAR, { compiler->variableIndex(compiler->field(VARIABLE)->valuePtr()) }); -} - -void VariableBlocks::compileSetVariable(Compiler *compiler) -{ - compiler->addInput(VALUE); - compiler->addInstruction(vm::OP_SET_VAR, { compiler->variableIndex(compiler->field(VARIABLE)->valuePtr()) }); -} - -void VariableBlocks::compileChangeVariableBy(Compiler *compiler) -{ - compiler->addInput(VALUE); - compiler->addInstruction(vm::OP_CHANGE_VAR, { compiler->variableIndex(compiler->field(VARIABLE)->valuePtr()) }); } -void VariableBlocks::compileShowVariable(Compiler *compiler) +CompilerValue *VariableBlocks::compileVariable(Compiler *compiler) { - Field *field = compiler->field(VARIABLE); - assert(field); - Variable *var = static_cast(field->valuePtr().get()); + Field *varField = compiler->field("VARIABLE"); + Variable *var = static_cast(varField->valuePtr().get()); assert(var); - compiler->addConstValue(var->id()); - - if (var->target() == static_cast(compiler->engine()->stage())) - compiler->addFunctionCall(&showGlobalVariable); + if (var) + return compiler->addVariableValue(var); else - compiler->addFunctionCall(&showVariable); + return compiler->addConstValue(Value()); } -void VariableBlocks::compileHideVariable(Compiler *compiler) +CompilerValue *VariableBlocks::compileSetVariableTo(Compiler *compiler) { - Field *field = compiler->field(VARIABLE); - assert(field); - Variable *var = static_cast(field->valuePtr().get()); + Field *varField = compiler->field("VARIABLE"); + Variable *var = static_cast(varField->valuePtr().get()); assert(var); - compiler->addConstValue(var->id()); - - if (var->target() == static_cast(compiler->engine()->stage())) - compiler->addFunctionCall(&hideGlobalVariable); - else - compiler->addFunctionCall(&hideVariable); -} - -void VariableBlocks::setVarVisible(std::shared_ptr var, bool visible, IEngine *engine) -{ - if (var) { - Monitor *monitor = var->monitor(); - - if (!monitor) - monitor = engine->createVariableMonitor(var, "data_variable", "VARIABLE", VARIABLE, &compileVariable); - - monitor->setVisible(visible); - } -} - -unsigned int VariableBlocks::showGlobalVariable(VirtualMachine *vm) -{ - if (Stage *target = vm->engine()->stage()) { - int index = target->findVariableById(vm->getInput(0, 1)->toString()); - setVarVisible(target->variableAt(index), true, vm->engine()); - } - - return 1; -} - -unsigned int VariableBlocks::showVariable(VirtualMachine *vm) -{ - if (Target *target = vm->target()) { - if (!target->isStage() && static_cast(target)->isClone()) { - Sprite *sprite = static_cast(target)->cloneSprite(); // use clone root variable - int index = sprite->findVariableById(vm->getInput(0, 1)->toString()); - setVarVisible(sprite->variableAt(index), true, vm->engine()); - } else { - int index = target->findVariableById(vm->getInput(0, 1)->toString()); - setVarVisible(target->variableAt(index), true, vm->engine()); - } - } - - return 1; -} - -unsigned int VariableBlocks::hideGlobalVariable(VirtualMachine *vm) -{ - if (Stage *target = vm->engine()->stage()) { - int index = target->findVariableById(vm->getInput(0, 1)->toString()); - setVarVisible(target->variableAt(index), false, vm->engine()); - } - - return 1; -} - -unsigned int VariableBlocks::hideVariable(VirtualMachine *vm) -{ - if (Target *target = vm->target()) { - if (!target->isStage() && static_cast(target)->isClone()) { - Sprite *sprite = static_cast(target)->cloneSprite(); // use clone root variable - int index = sprite->findVariableById(vm->getInput(0, 1)->toString()); - setVarVisible(sprite->variableAt(index), false, vm->engine()); - } else { - int index = target->findVariableById(vm->getInput(0, 1)->toString()); - setVarVisible(target->variableAt(index), false, vm->engine()); - } - } + if (var) + compiler->createVariableWrite(var, compiler->addInput("VALUE")); - return 1; + return nullptr; } -const std::string &VariableBlocks::variableMonitorName(Block *block) +CompilerValue *VariableBlocks::compileChangeVariableBy(Compiler *compiler) { - Variable *var = dynamic_cast(block->findFieldById(VARIABLE)->valuePtr().get()); + Field *varField = compiler->field("VARIABLE"); + Variable *var = static_cast(varField->valuePtr().get()); + assert(var); - if (var) - return var->name(); - else { - static const std::string empty = ""; - return empty; + if (var) { + CompilerValue *value = compiler->createAdd(compiler->addVariableValue(var), compiler->addInput("VALUE")); + compiler->createVariableWrite(var, value); } -} -void VariableBlocks::changeVariableMonitorValue(Block *block, const Value &newValue) -{ - Variable *var = dynamic_cast(block->findFieldById(VARIABLE)->valuePtr().get()); - - if (var) - var->setValue(newValue); + return nullptr; } diff --git a/src/blocks/variableblocks.h b/src/blocks/variableblocks.h index 2372660b..9cb0bcc4 100644 --- a/src/blocks/variableblocks.h +++ b/src/blocks/variableblocks.h @@ -3,48 +3,22 @@ #pragma once #include -#include namespace libscratchcpp { -class Compiler; -class Variable; - -/*! \brief The VariableBlocks class contains the implementation of variable blocks. */ class VariableBlocks : public IExtension { public: - enum Inputs - { - VALUE - }; - - enum Fields - { - VARIABLE - }; - std::string name() const override; std::string description() const override; void registerBlocks(IEngine *engine) override; - static void compileVariable(Compiler *compiler); - static void compileSetVariable(Compiler *compiler); - static void compileChangeVariableBy(Compiler *compiler); - static void compileShowVariable(Compiler *compiler); - static void compileHideVariable(Compiler *compiler); - - static void setVarVisible(std::shared_ptr var, bool visible, IEngine *engine); - - static unsigned int showGlobalVariable(VirtualMachine *vm); - static unsigned int showVariable(VirtualMachine *vm); - static unsigned int hideGlobalVariable(VirtualMachine *vm); - static unsigned int hideVariable(VirtualMachine *vm); - - static const std::string &variableMonitorName(Block *block); - static void changeVariableMonitorValue(Block *block, const Value &newValue); + private: + static CompilerValue *compileVariable(Compiler *compiler); + static CompilerValue *compileSetVariableTo(Compiler *compiler); + static CompilerValue *compileChangeVariableBy(Compiler *compiler); }; } // namespace libscratchcpp diff --git a/src/dev/CMakeLists.txt b/src/dev/CMakeLists.txt deleted file mode 100644 index 4c9b3cd6..00000000 --- a/src/dev/CMakeLists.txt +++ /dev/null @@ -1,3 +0,0 @@ -add_subdirectory(blocks) -add_subdirectory(engine) -add_subdirectory(test) diff --git a/src/dev/blocks/CMakeLists.txt b/src/dev/blocks/CMakeLists.txt deleted file mode 100644 index d7416856..00000000 --- a/src/dev/blocks/CMakeLists.txt +++ /dev/null @@ -1,115 +0,0 @@ -target_sources(scratchcpp - PRIVATE - blocks.cpp - blocks.h -) - -# Motion blocks -option(LIBSCRATCHCPP_ENABLE_MOTION_BLOCKS "Motion blocks support" ON) -if (LIBSCRATCHCPP_ENABLE_MOTION_BLOCKS) - target_compile_definitions(scratchcpp PRIVATE ENABLE_MOTION_BLOCKS) - target_sources(scratchcpp - PRIVATE - motionblocks.cpp - motionblocks.h - ) -endif() - -# Looks blocks -option(LIBSCRATCHCPP_ENABLE_LOOKS_BLOCKS "Looks blocks support" ON) -if (LIBSCRATCHCPP_ENABLE_LOOKS_BLOCKS) - target_compile_definitions(scratchcpp PRIVATE ENABLE_LOOKS_BLOCKS) - target_sources(scratchcpp - PRIVATE - looksblocks.cpp - looksblocks.h - ) -endif() - -# Sound blocks -option(LIBSCRATCHCPP_ENABLE_SOUND_BLOCKS "Sound blocks support" ON) -if (LIBSCRATCHCPP_ENABLE_SOUND_BLOCKS) - target_compile_definitions(scratchcpp PRIVATE ENABLE_SOUND_BLOCKS) - target_sources(scratchcpp - PRIVATE - soundblocks.cpp - soundblocks.h - ) -endif() - -# Event blocks -option(LIBSCRATCHCPP_ENABLE_EVENT_BLOCKS "Event blocks support" ON) -if (LIBSCRATCHCPP_ENABLE_EVENT_BLOCKS) - target_compile_definitions(scratchcpp PRIVATE ENABLE_EVENT_BLOCKS) - target_sources(scratchcpp - PRIVATE - eventblocks.cpp - eventblocks.h - ) -endif() - -# Control blocks -option(LIBSCRATCHCPP_ENABLE_CONTROL_BLOCKS "Control blocks support" ON) -if (LIBSCRATCHCPP_ENABLE_CONTROL_BLOCKS) - target_compile_definitions(scratchcpp PRIVATE ENABLE_CONTROL_BLOCKS) - target_sources(scratchcpp - PRIVATE - controlblocks.cpp - controlblocks.h - ) -endif() - -# Sensing blocks -option(LIBSCRATCHCPP_ENABLE_SENSING_BLOCKS "Sensing blocks support" ON) -if (LIBSCRATCHCPP_ENABLE_SENSING_BLOCKS) - target_compile_definitions(scratchcpp PRIVATE ENABLE_SENSING_BLOCKS) - target_sources(scratchcpp - PRIVATE - sensingblocks.cpp - sensingblocks.h - ) -endif() - -# Operator blocks -option(LIBSCRATCHCPP_ENABLE_OPERATOR_BLOCKS "Operator blocks support" ON) -if (LIBSCRATCHCPP_ENABLE_OPERATOR_BLOCKS) - target_compile_definitions(scratchcpp PRIVATE ENABLE_OPERATOR_BLOCKS) - target_sources(scratchcpp - PRIVATE - operatorblocks.cpp - operatorblocks.h - ) -endif() - -# Variable blocks -option(LIBSCRATCHCPP_ENABLE_VARIABLE_BLOCKS "Variable blocks support" ON) -if (LIBSCRATCHCPP_ENABLE_VARIABLE_BLOCKS) - target_compile_definitions(scratchcpp PRIVATE ENABLE_VARIABLE_BLOCKS) - target_sources(scratchcpp - PRIVATE - variableblocks.cpp - variableblocks.h - ) -endif() - -# List blocks -option(LIBSCRATCHCPP_ENABLE_LIST_BLOCKS "List blocks support" ON) -if (LIBSCRATCHCPP_ENABLE_LIST_BLOCKS) - target_compile_definitions(scratchcpp PRIVATE ENABLE_LIST_BLOCKS) - target_sources(scratchcpp - PRIVATE - listblocks.cpp - listblocks.h - ) -endif() - -# Custom blocks -option(LIBSCRATCHCPP_ENABLE_CUSTOM_BLOCKS "Custom blocks support" ON) -if (LIBSCRATCHCPP_ENABLE_CUSTOM_BLOCKS) - target_compile_definitions(scratchcpp PRIVATE ENABLE_CUSTOM_BLOCKS) - target_sources(scratchcpp - PRIVATE - customblocks.cpp - customblocks.h - ) -endif() diff --git a/src/dev/blocks/blocks.cpp b/src/dev/blocks/blocks.cpp deleted file mode 100644 index c352547b..00000000 --- a/src/dev/blocks/blocks.cpp +++ /dev/null @@ -1,85 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 - -#include - -#include "blocks.h" - -#ifdef ENABLE_MOTION_BLOCKS -#include "motionblocks.h" -#endif -#ifdef ENABLE_LOOKS_BLOCKS -#include "looksblocks.h" -#endif -#ifdef ENABLE_SOUND_BLOCKS -#include "soundblocks.h" -#endif -#ifdef ENABLE_EVENT_BLOCKS -#include "eventblocks.h" -#endif -#ifdef ENABLE_CONTROL_BLOCKS -#include "controlblocks.h" -#endif -#ifdef ENABLE_SENSING_BLOCKS -#include "sensingblocks.h" -#endif -#ifdef ENABLE_OPERATOR_BLOCKS -#include "operatorblocks.h" -#endif -#ifdef ENABLE_VARIABLE_BLOCKS -#include "variableblocks.h" -#endif -#ifdef ENABLE_LIST_BLOCKS -#include "listblocks.h" -#endif -#ifdef ENABLE_CUSTOM_BLOCKS -#include "customblocks.h" -#endif - -using namespace libscratchcpp; - -Blocks Blocks::m_instance; - -const std::vector> &Blocks::extensions() -{ - return m_instance.m_extensions; -} - -Blocks::Blocks() -{ -#ifdef ENABLE_MOTION_BLOCKS - m_extensions.push_back(std::make_shared()); -#endif -#ifdef ENABLE_LOOKS_BLOCKS - m_extensions.push_back(std::make_shared()); -#endif -#ifdef ENABLE_SOUND_BLOCKS - m_extensions.push_back(std::make_shared()); -#endif -#ifdef ENABLE_EVENT_BLOCKS - m_extensions.push_back(std::make_shared()); -#endif -#ifdef ENABLE_CONTROL_BLOCKS - m_extensions.push_back(std::make_shared()); -#endif -#ifdef ENABLE_SENSING_BLOCKS - m_extensions.push_back(std::make_shared()); -#endif -#ifdef ENABLE_OPERATOR_BLOCKS - m_extensions.push_back(std::make_shared()); -#endif -#ifdef ENABLE_VARIABLE_BLOCKS - m_extensions.push_back(std::make_shared()); -#endif -#ifdef ENABLE_LIST_BLOCKS - m_extensions.push_back(std::make_shared()); -#endif -#ifdef ENABLE_CUSTOM_BLOCKS - m_extensions.push_back(std::make_shared()); -#endif -} - -void Blocks::registerExtensions() -{ - for (auto ext : m_extensions) - ScratchConfiguration::registerExtension(ext); -} diff --git a/src/dev/blocks/blocks.h b/src/dev/blocks/blocks.h deleted file mode 100644 index 56be7a34..00000000 --- a/src/dev/blocks/blocks.h +++ /dev/null @@ -1,26 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 - -#pragma once - -#include -#include - -namespace libscratchcpp -{ - -class IExtension; - -class Blocks -{ - public: - static const std::vector> &extensions(); - - private: - Blocks(); - void registerExtensions(); - - static Blocks m_instance; // use static initialization - std::vector> m_extensions; -}; - -} // namespace libscratchcpp diff --git a/src/dev/blocks/controlblocks.cpp b/src/dev/blocks/controlblocks.cpp deleted file mode 100644 index 9567f69f..00000000 --- a/src/dev/blocks/controlblocks.cpp +++ /dev/null @@ -1,233 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "controlblocks.h" - -using namespace libscratchcpp; - -std::string ControlBlocks::name() const -{ - return "Control"; -} - -std::string ControlBlocks::description() const -{ - return name() + " blocks"; -} - -void ControlBlocks::registerBlocks(IEngine *engine) -{ - engine->addCompileFunction(this, "control_forever", &compileForever); - engine->addCompileFunction(this, "control_repeat", &compileRepeat); - engine->addCompileFunction(this, "control_if", &compileIf); - engine->addCompileFunction(this, "control_if_else", &compileIfElse); - engine->addCompileFunction(this, "control_stop", &compileStop); - engine->addCompileFunction(this, "control_wait", &compileWait); - engine->addCompileFunction(this, "control_wait_until", &compileWaitUntil); - engine->addCompileFunction(this, "control_repeat_until", &compileRepeatUntil); - engine->addCompileFunction(this, "control_while", &compileWhile); - engine->addCompileFunction(this, "control_for_each", &compileForEach); - engine->addCompileFunction(this, "control_start_as_clone", &compileStartAsClone); - engine->addCompileFunction(this, "control_create_clone_of", &compileCreateCloneOf); - engine->addCompileFunction(this, "control_delete_this_clone", &compileDeleteThisClone); -} - -CompilerValue *ControlBlocks::compileForever(Compiler *compiler) -{ - auto substack = compiler->input("SUBSTACK"); - compiler->beginLoopCondition(); - compiler->moveToWhileLoop(compiler->addConstValue(true), substack ? substack->valueBlock() : nullptr); - return nullptr; -} - -CompilerValue *ControlBlocks::compileRepeat(Compiler *compiler) -{ - auto substack = compiler->input("SUBSTACK"); - compiler->moveToRepeatLoop(compiler->addInput("TIMES"), substack ? substack->valueBlock() : nullptr); - return nullptr; -} - -CompilerValue *ControlBlocks::compileIf(Compiler *compiler) -{ - auto substack = compiler->input("SUBSTACK"); - compiler->moveToIf(compiler->addInput("CONDITION"), substack ? substack->valueBlock() : nullptr); - return nullptr; -} - -CompilerValue *ControlBlocks::compileIfElse(Compiler *compiler) -{ - auto substack = compiler->input("SUBSTACK"); - auto substack2 = compiler->input("SUBSTACK2"); - compiler->moveToIfElse(compiler->addInput("CONDITION"), substack ? substack->valueBlock() : nullptr, substack2 ? substack2->valueBlock() : nullptr); - return nullptr; -} - -CompilerValue *ControlBlocks::compileStop(Compiler *compiler) -{ - Field *option = compiler->field("STOP_OPTION"); - - if (option) { - std::string str = option->value().toString(); - - if (str == "all") - compiler->addFunctionCallWithCtx("control_stop_all", Compiler::StaticType::Void); - else if (str == "this script") - compiler->createStop(); - else if (str == "other scripts in sprite" || str == "other scripts in stage") - compiler->addFunctionCallWithCtx("control_stop_other_scripts_in_target", Compiler::StaticType::Void); - } - - return nullptr; -} - -CompilerValue *ControlBlocks::compileWait(Compiler *compiler) -{ - auto duration = compiler->addInput("DURATION"); - compiler->addFunctionCallWithCtx("control_start_wait", Compiler::StaticType::Void, { Compiler::StaticType::Number }, { duration }); - compiler->createYield(); - - compiler->beginLoopCondition(); - auto elapsed = compiler->addFunctionCallWithCtx("control_stack_timer_elapsed", Compiler::StaticType::Bool); - compiler->beginRepeatUntilLoop(elapsed); - compiler->endLoop(); - - return nullptr; -} - -CompilerValue *ControlBlocks::compileWaitUntil(Compiler *compiler) -{ - compiler->beginLoopCondition(); - compiler->beginRepeatUntilLoop(compiler->addInput("CONDITION")); - compiler->endLoop(); - return nullptr; -} - -CompilerValue *ControlBlocks::compileRepeatUntil(Compiler *compiler) -{ - auto substack = compiler->input("SUBSTACK"); - compiler->beginLoopCondition(); - compiler->moveToRepeatUntilLoop(compiler->addInput("CONDITION"), substack ? substack->valueBlock() : nullptr); - return nullptr; -} - -CompilerValue *ControlBlocks::compileWhile(Compiler *compiler) -{ - auto substack = compiler->input("SUBSTACK"); - compiler->beginLoopCondition(); - compiler->moveToWhileLoop(compiler->addInput("CONDITION"), substack ? substack->valueBlock() : nullptr); - return nullptr; -} - -CompilerValue *ControlBlocks::compileForEach(Compiler *compiler) -{ - Variable *var = static_cast(compiler->field("VARIABLE")->valuePtr().get()); - assert(var); - auto substack = compiler->input("SUBSTACK"); - compiler->moveToRepeatLoop(compiler->addInput("VALUE"), substack ? substack->valueBlock() : nullptr); - auto index = compiler->createAdd(compiler->addLoopIndex(), compiler->addConstValue(1)); - compiler->createVariableWrite(var, index); - return nullptr; -} - -CompilerValue *ControlBlocks::compileStartAsClone(Compiler *compiler) -{ - compiler->engine()->addCloneInitScript(compiler->block()); - return nullptr; -} - -CompilerValue *ControlBlocks::compileCreateCloneOf(Compiler *compiler) -{ - Input *input = compiler->input("CLONE_OPTION"); - - if (input->pointsToDropdownMenu()) { - std::string spriteName = input->selectedMenuItem(); - - if (spriteName == "_myself_") - compiler->addTargetFunctionCall("control_create_clone_of_myself"); - else { - auto index = compiler->engine()->findTarget(spriteName); - CompilerValue *arg = compiler->addConstValue(index); - compiler->addFunctionCallWithCtx("control_create_clone_by_index", Compiler::StaticType::Void, { Compiler::StaticType::Number }, { arg }); - } - } else { - CompilerValue *arg = compiler->addInput("CLONE_OPTION"); - compiler->addFunctionCallWithCtx("control_create_clone", Compiler::StaticType::Void, { Compiler::StaticType::String }, { arg }); - } - - return nullptr; -} - -CompilerValue *ControlBlocks::compileDeleteThisClone(Compiler *compiler) -{ - compiler->addTargetFunctionCall("control_delete_this_clone"); - return nullptr; -} - -extern "C" void control_stop_all(ExecutionContext *ctx) -{ - ctx->engine()->stop(); -} - -extern "C" void control_stop_other_scripts_in_target(ExecutionContext *ctx) -{ - Thread *thread = ctx->thread(); - ctx->engine()->stopTarget(thread->target(), thread); -} - -extern "C" void control_start_wait(ExecutionContext *ctx, double seconds) -{ - ctx->stackTimer()->start(seconds); - ctx->engine()->requestRedraw(); -} - -extern "C" bool control_stack_timer_elapsed(ExecutionContext *ctx) -{ - return ctx->stackTimer()->elapsed(); -} - -extern "C" void control_create_clone_of_myself(Target *target) -{ - if (!target->isStage()) - static_cast(target)->clone(); -} - -extern "C" void control_create_clone_by_index(ExecutionContext *ctx, double index) -{ - Target *target = ctx->engine()->targetAt(index); - - if (!target->isStage()) - static_cast(target)->clone(); -} - -extern "C" void control_create_clone(ExecutionContext *ctx, const char *spriteName) -{ - if (strcmp(spriteName, "_myself_") == 0) - control_create_clone_of_myself(ctx->thread()->target()); - else { - IEngine *engine = ctx->engine(); - auto index = engine->findTarget(spriteName); - Target *target = engine->targetAt(index); - - if (!target->isStage()) - static_cast(target)->clone(); - } -} - -extern "C" void control_delete_this_clone(Target *target) -{ - if (!target->isStage()) { - target->engine()->stopTarget(target, nullptr); - static_cast(target)->deleteClone(); - } -} diff --git a/src/dev/blocks/controlblocks.h b/src/dev/blocks/controlblocks.h deleted file mode 100644 index 17850824..00000000 --- a/src/dev/blocks/controlblocks.h +++ /dev/null @@ -1,34 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 - -#pragma once - -#include - -namespace libscratchcpp -{ - -class ControlBlocks : public IExtension -{ - public: - std::string name() const override; - std::string description() const override; - - void registerBlocks(IEngine *engine) override; - - private: - static CompilerValue *compileForever(Compiler *compiler); - static CompilerValue *compileRepeat(Compiler *compiler); - static CompilerValue *compileIf(Compiler *compiler); - static CompilerValue *compileIfElse(Compiler *compiler); - static CompilerValue *compileStop(Compiler *compiler); - static CompilerValue *compileWait(Compiler *compiler); - static CompilerValue *compileWaitUntil(Compiler *compiler); - static CompilerValue *compileRepeatUntil(Compiler *compiler); - static CompilerValue *compileWhile(Compiler *compiler); - static CompilerValue *compileForEach(Compiler *compiler); - static CompilerValue *compileStartAsClone(Compiler *compiler); - static CompilerValue *compileCreateCloneOf(Compiler *compiler); - static CompilerValue *compileDeleteThisClone(Compiler *compiler); -}; - -} // namespace libscratchcpp diff --git a/src/dev/blocks/customblocks.cpp b/src/dev/blocks/customblocks.cpp deleted file mode 100644 index e78048bc..00000000 --- a/src/dev/blocks/customblocks.cpp +++ /dev/null @@ -1,55 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 - -#include -#include -#include -#include -#include - -#include "customblocks.h" - -using namespace libscratchcpp; - -std::string CustomBlocks::name() const -{ - return "Custom blocks"; -} - -std::string CustomBlocks::description() const -{ - return name(); -} - -void CustomBlocks::registerBlocks(IEngine *engine) -{ - engine->addCompileFunction(this, "procedures_definition", [](Compiler *) -> CompilerValue * { return nullptr; }); - engine->addCompileFunction(this, "procedures_call", &compileCall); - engine->addCompileFunction(this, "argument_reporter_boolean", &compileArgument); - engine->addCompileFunction(this, "argument_reporter_string_number", &compileArgument); -} - -CompilerValue *CustomBlocks::compileCall(Compiler *compiler) -{ - auto block = compiler->block(); - auto prototype = block->mutationPrototype(); - const std::vector &procedureArgs = prototype->argumentIds(); - Compiler::Args args; - - for (size_t i = 0; i < procedureArgs.size(); i++) { - auto index = block->findInput(procedureArgs[i]); - - if (index == -1) - args.push_back(compiler->addConstValue(Value())); - else - args.push_back(compiler->addInput(block->inputAt(index).get())); - } - - compiler->createProcedureCall(compiler->block()->mutationPrototype(), args); - return nullptr; -} - -CompilerValue *CustomBlocks::compileArgument(Compiler *compiler) -{ - const std::string &argName = compiler->field("VALUE")->value().toString(); - return compiler->addProcedureArgument(argName); -} diff --git a/src/dev/blocks/customblocks.h b/src/dev/blocks/customblocks.h deleted file mode 100644 index 55572ba9..00000000 --- a/src/dev/blocks/customblocks.h +++ /dev/null @@ -1,23 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 - -#pragma once - -#include - -namespace libscratchcpp -{ - -class CustomBlocks : public IExtension -{ - public: - std::string name() const override; - std::string description() const override; - - void registerBlocks(IEngine *engine) override; - - private: - static CompilerValue *compileCall(Compiler *compiler); - static CompilerValue *compileArgument(Compiler *compiler); -}; - -} // namespace libscratchcpp diff --git a/src/dev/blocks/eventblocks.cpp b/src/dev/blocks/eventblocks.cpp deleted file mode 100644 index a2b18b3b..00000000 --- a/src/dev/blocks/eventblocks.cpp +++ /dev/null @@ -1,134 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "eventblocks.h" - -using namespace libscratchcpp; - -std::string EventBlocks::name() const -{ - return "Events"; -} - -std::string libscratchcpp::EventBlocks::description() const -{ - return "Event blocks"; -} - -void EventBlocks::registerBlocks(IEngine *engine) -{ - engine->addCompileFunction(this, "event_whentouchingobject", &compileWhenTouchingObject); - engine->addCompileFunction(this, "event_whenflagclicked", &compileWhenFlagClicked); - engine->addCompileFunction(this, "event_whenthisspriteclicked", &compileWhenThisSpriteClicked); - engine->addCompileFunction(this, "event_whenstageclicked", &compileWhenStageClicked); - engine->addCompileFunction(this, "event_whenbroadcastreceived", &compileWhenBroadcastReceived); - engine->addCompileFunction(this, "event_whenbackdropswitchesto", &compileWhenBackdropSwitchesTo); - engine->addCompileFunction(this, "event_whengreaterthan", &compileWhenGreaterThan); - engine->addCompileFunction(this, "event_broadcast", &compileBroadcast); - engine->addCompileFunction(this, "event_broadcastandwait", &compileBroadcastAndWait); - engine->addCompileFunction(this, "event_whenkeypressed", &compileWhenKeyPressed); -} - -CompilerValue *EventBlocks::compileWhenTouchingObject(Compiler *compiler) -{ - compiler->engine()->addWhenTouchingObjectScript(compiler->block()); - return nullptr; -} - -CompilerValue *EventBlocks::compileWhenFlagClicked(Compiler *compiler) -{ - compiler->engine()->addGreenFlagScript(compiler->block()); - return nullptr; -} - -CompilerValue *EventBlocks::compileWhenThisSpriteClicked(Compiler *compiler) -{ - compiler->engine()->addTargetClickScript(compiler->block()); - return nullptr; -} - -CompilerValue *EventBlocks::compileWhenStageClicked(Compiler *compiler) -{ - compiler->engine()->addTargetClickScript(compiler->block()); - return nullptr; -} - -CompilerValue *EventBlocks::compileWhenBroadcastReceived(Compiler *compiler) -{ - auto block = compiler->block(); - Field *field = compiler->field("BROADCAST_OPTION"); - - if (field) { - auto broadcast = std::static_pointer_cast(field->valuePtr()); - compiler->engine()->addBroadcastScript(block, field, broadcast.get()); - } - - return nullptr; -} - -CompilerValue *EventBlocks::compileWhenBackdropSwitchesTo(Compiler *compiler) -{ - auto block = compiler->block(); - Field *field = compiler->field("BACKDROP"); - - if (field) - compiler->engine()->addBackdropChangeScript(block, field); - - return nullptr; -} - -CompilerValue *EventBlocks::compileWhenGreaterThan(Compiler *compiler) -{ - compiler->engine()->addWhenGreaterThanScript(compiler->block()); - return nullptr; -} - -CompilerValue *EventBlocks::compileBroadcast(Compiler *compiler) -{ - auto input = compiler->addInput("BROADCAST_INPUT"); - auto wait = compiler->addConstValue(false); - compiler->addFunctionCallWithCtx("event_broadcast", Compiler::StaticType::Void, { Compiler::StaticType::String, Compiler::StaticType::Bool }, { input, wait }); - return nullptr; -} - -CompilerValue *EventBlocks::compileBroadcastAndWait(Compiler *compiler) -{ - auto input = compiler->addInput("BROADCAST_INPUT"); - auto wait = compiler->addConstValue(true); - compiler->addFunctionCallWithCtx("event_broadcast", Compiler::StaticType::Void, { Compiler::StaticType::String, Compiler::StaticType::Bool }, { input, wait }); - compiler->createYield(); - return nullptr; -} - -CompilerValue *EventBlocks::compileWhenKeyPressed(Compiler *compiler) -{ - auto block = compiler->block(); - Field *field = compiler->field("KEY_OPTION"); - - if (field) - compiler->engine()->addKeyPressScript(block, field); - - return nullptr; -} - -extern "C" void event_broadcast(ExecutionContext *ctx, const char *name, bool wait) -{ - Thread *thread = ctx->thread(); - IEngine *engine = thread->engine(); - std::vector broadcasts = engine->findBroadcasts(name); - - for (int index : broadcasts) - engine->broadcast(index, thread, wait); - - if (wait) - ctx->setPromise(std::make_shared()); -} diff --git a/src/dev/blocks/eventblocks.h b/src/dev/blocks/eventblocks.h deleted file mode 100644 index 34454ebc..00000000 --- a/src/dev/blocks/eventblocks.h +++ /dev/null @@ -1,31 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 - -#pragma once - -#include - -namespace libscratchcpp -{ - -class EventBlocks : public IExtension -{ - public: - std::string name() const override; - std::string description() const override; - - void registerBlocks(IEngine *engine) override; - - private: - static CompilerValue *compileWhenTouchingObject(Compiler *compiler); - static CompilerValue *compileWhenFlagClicked(Compiler *compiler); - static CompilerValue *compileWhenThisSpriteClicked(Compiler *compiler); - static CompilerValue *compileWhenStageClicked(Compiler *compiler); - static CompilerValue *compileWhenBroadcastReceived(Compiler *compiler); - static CompilerValue *compileWhenBackdropSwitchesTo(Compiler *compiler); - static CompilerValue *compileWhenGreaterThan(Compiler *compiler); - static CompilerValue *compileBroadcast(Compiler *compiler); - static CompilerValue *compileBroadcastAndWait(Compiler *compiler); - static CompilerValue *compileWhenKeyPressed(Compiler *compiler); -}; - -} // namespace libscratchcpp diff --git a/src/dev/blocks/listblocks.cpp b/src/dev/blocks/listblocks.cpp deleted file mode 100644 index 653875e4..00000000 --- a/src/dev/blocks/listblocks.cpp +++ /dev/null @@ -1,191 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 - -#include -#include -#include -#include -#include - -#include "listblocks.h" - -using namespace libscratchcpp; - -std::string ListBlocks::name() const -{ - return "Lists"; -} - -std::string ListBlocks::description() const -{ - return "List blocks"; -} - -void ListBlocks::registerBlocks(IEngine *engine) -{ - engine->addCompileFunction(this, "data_addtolist", &compileAddToList); - engine->addCompileFunction(this, "data_deleteoflist", &compileDeleteOfList); - engine->addCompileFunction(this, "data_deletealloflist", &compileDeleteAllOfList); - engine->addCompileFunction(this, "data_insertatlist", &compileInsertAtList); - engine->addCompileFunction(this, "data_replaceitemoflist", &compileReplaceItemOfList); - engine->addCompileFunction(this, "data_itemoflist", &compileItemOfList); - engine->addCompileFunction(this, "data_itemnumoflist", &compileItemNumOfList); - engine->addCompileFunction(this, "data_lengthoflist", &compileLengthOfList); - engine->addCompileFunction(this, "data_listcontainsitem", &compileListContainsItem); -} - -CompilerValue *ListBlocks::compileAddToList(Compiler *compiler) -{ - auto list = compiler->field("LIST")->valuePtr(); - assert(list); - - if (list) - compiler->createListAppend(static_cast(list.get()), compiler->addInput("ITEM")); - - return nullptr; -} - -CompilerValue *ListBlocks::getListIndex(Compiler *compiler, CompilerValue *input, List *list, CompilerValue *listSize) -{ - CompilerLocalVariable *ret = compiler->createLocalVariable(Compiler::StaticType::Number); - - CompilerValue *isRandom1 = compiler->createStrCmpEQ(input, compiler->addConstValue("random"), true); - CompilerValue *isRandom2 = compiler->createStrCmpEQ(input, compiler->addConstValue("any"), true); - CompilerValue *isRandom = compiler->createOr(isRandom1, isRandom2); - - compiler->beginIfStatement(isRandom); - { - CompilerValue *random = compiler->createRandomInt(compiler->addConstValue(1), listSize); - compiler->createLocalVariableWrite(ret, random); - } - compiler->beginElseBranch(); - { - CompilerValue *isLast = compiler->createStrCmpEQ(input, compiler->addConstValue("last"), true); - compiler->createLocalVariableWrite(ret, compiler->createSelect(isLast, listSize, input, Compiler::StaticType::Number)); - } - compiler->endIf(); - - return compiler->addLocalVariableValue(ret); -} - -CompilerValue *ListBlocks::compileDeleteOfList(Compiler *compiler) -{ - List *list = static_cast(compiler->field("LIST")->valuePtr().get()); - assert(list); - - if (list) { - CompilerValue *index = compiler->addInput("INDEX"); - CompilerValue *cond = compiler->createStrCmpEQ(index, compiler->addConstValue("all"), true); - compiler->beginIfStatement(cond); - { - compiler->createListClear(list); - } - compiler->beginElseBranch(); - { - CompilerValue *max = compiler->addListSize(list); - index = getListIndex(compiler, index, list, max); - index = compiler->createSub(index, compiler->addConstValue(1)); - compiler->createListRemove(list, index); - } - compiler->endIf(); - } - - return nullptr; -} - -CompilerValue *ListBlocks::compileDeleteAllOfList(Compiler *compiler) -{ - auto list = compiler->field("LIST")->valuePtr(); - assert(list); - - if (list) - compiler->createListClear(static_cast(list.get())); - - return nullptr; -} - -CompilerValue *ListBlocks::compileInsertAtList(Compiler *compiler) -{ - List *list = static_cast(compiler->field("LIST")->valuePtr().get()); - assert(list); - - if (list) { - CompilerValue *index = compiler->addInput("INDEX"); - CompilerValue *max = compiler->createAdd(compiler->addListSize(list), compiler->addConstValue(1)); - index = getListIndex(compiler, index, list, max); - index = compiler->createSub(index, compiler->addConstValue(1)); - CompilerValue *item = compiler->addInput("ITEM"); - compiler->createListInsert(list, index, item); - } - - return nullptr; -} - -CompilerValue *ListBlocks::compileReplaceItemOfList(Compiler *compiler) -{ - List *list = static_cast(compiler->field("LIST")->valuePtr().get()); - assert(list); - - if (list) { - CompilerValue *index = compiler->addInput("INDEX"); - CompilerValue *max = compiler->addListSize(list); - index = getListIndex(compiler, index, list, max); - index = compiler->createSub(index, compiler->addConstValue(1)); - CompilerValue *item = compiler->addInput("ITEM"); - compiler->createListReplace(list, index, item); - } - - return nullptr; -} - -CompilerValue *ListBlocks::compileItemOfList(Compiler *compiler) -{ - List *list = static_cast(compiler->field("LIST")->valuePtr().get()); - assert(list); - - if (list) { - CompilerValue *index = compiler->addInput("INDEX"); - CompilerValue *max = compiler->addListSize(list); - index = getListIndex(compiler, index, list, max); - index = compiler->createSub(index, compiler->addConstValue(1)); - return compiler->addListItem(list, index); - } - - return nullptr; -} - -CompilerValue *ListBlocks::compileItemNumOfList(Compiler *compiler) -{ - List *list = static_cast(compiler->field("LIST")->valuePtr().get()); - assert(list); - - if (list) { - CompilerValue *item = compiler->addInput("ITEM"); - return compiler->createAdd(compiler->addListItemIndex(list, item), compiler->addConstValue(1)); - } - - return nullptr; -} - -CompilerValue *ListBlocks::compileLengthOfList(Compiler *compiler) -{ - List *list = static_cast(compiler->field("LIST")->valuePtr().get()); - assert(list); - - if (list) - return compiler->addListSize(list); - - return nullptr; -} - -CompilerValue *ListBlocks::compileListContainsItem(Compiler *compiler) -{ - List *list = static_cast(compiler->field("LIST")->valuePtr().get()); - assert(list); - - if (list) { - CompilerValue *item = compiler->addInput("ITEM"); - return compiler->addListContains(list, item); - } - - return nullptr; -} diff --git a/src/dev/blocks/listblocks.h b/src/dev/blocks/listblocks.h deleted file mode 100644 index 3a8d0126..00000000 --- a/src/dev/blocks/listblocks.h +++ /dev/null @@ -1,33 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 - -#pragma once - -#include - -namespace libscratchcpp -{ - -class List; - -class ListBlocks : public IExtension -{ - public: - std::string name() const override; - std::string description() const override; - - void registerBlocks(IEngine *engine) override; - - private: - static CompilerValue *compileAddToList(Compiler *compiler); - static CompilerValue *getListIndex(Compiler *compiler, CompilerValue *input, List *list, CompilerValue *listSize); - static CompilerValue *compileDeleteOfList(Compiler *compiler); - static CompilerValue *compileDeleteAllOfList(Compiler *compiler); - static CompilerValue *compileInsertAtList(Compiler *compiler); - static CompilerValue *compileReplaceItemOfList(Compiler *compiler); - static CompilerValue *compileItemOfList(Compiler *compiler); - static CompilerValue *compileItemNumOfList(Compiler *compiler); - static CompilerValue *compileLengthOfList(Compiler *compiler); - static CompilerValue *compileListContainsItem(Compiler *compiler); -}; - -} // namespace libscratchcpp diff --git a/src/dev/blocks/looksblocks.cpp b/src/dev/blocks/looksblocks.cpp deleted file mode 100644 index 0d471fb0..00000000 --- a/src/dev/blocks/looksblocks.cpp +++ /dev/null @@ -1,19 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 - -#include "looksblocks.h" - -using namespace libscratchcpp; - -std::string LooksBlocks::name() const -{ - return "Looks"; -} - -std::string LooksBlocks::description() const -{ - return name() + " blocks"; -} - -void LooksBlocks::registerBlocks(IEngine *engine) -{ -} diff --git a/src/dev/blocks/looksblocks.h b/src/dev/blocks/looksblocks.h deleted file mode 100644 index 8bfa5f18..00000000 --- a/src/dev/blocks/looksblocks.h +++ /dev/null @@ -1,19 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 - -#pragma once - -#include - -namespace libscratchcpp -{ - -class LooksBlocks : public IExtension -{ - public: - std::string name() const override; - std::string description() const override; - - void registerBlocks(IEngine *engine) override; -}; - -} // namespace libscratchcpp diff --git a/src/dev/blocks/motionblocks.cpp b/src/dev/blocks/motionblocks.cpp deleted file mode 100644 index e44a673c..00000000 --- a/src/dev/blocks/motionblocks.cpp +++ /dev/null @@ -1,19 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 - -#include "motionblocks.h" - -using namespace libscratchcpp; - -std::string MotionBlocks::name() const -{ - return "Motion"; -} - -std::string MotionBlocks::description() const -{ - return name() + " blocks"; -} - -void MotionBlocks::registerBlocks(IEngine *engine) -{ -} diff --git a/src/dev/blocks/motionblocks.h b/src/dev/blocks/motionblocks.h deleted file mode 100644 index 125b4073..00000000 --- a/src/dev/blocks/motionblocks.h +++ /dev/null @@ -1,19 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 - -#pragma once - -#include - -namespace libscratchcpp -{ - -class MotionBlocks : public IExtension -{ - public: - std::string name() const override; - std::string description() const override; - - void registerBlocks(IEngine *engine) override; -}; - -} // namespace libscratchcpp diff --git a/src/dev/blocks/operatorblocks.cpp b/src/dev/blocks/operatorblocks.cpp deleted file mode 100644 index b962d3c7..00000000 --- a/src/dev/blocks/operatorblocks.cpp +++ /dev/null @@ -1,228 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 - -#include -#include -#include -#include -#include -#include - -#include "operatorblocks.h" - -using namespace libscratchcpp; - -std::string OperatorBlocks::name() const -{ - return "Operators"; -} - -std::string OperatorBlocks::description() const -{ - return "Operator blocks"; -} - -void OperatorBlocks::registerBlocks(IEngine *engine) -{ - engine->addCompileFunction(this, "operator_add", &compileAdd); - engine->addCompileFunction(this, "operator_subtract", &compileSubtract); - engine->addCompileFunction(this, "operator_multiply", &compileMultiply); - engine->addCompileFunction(this, "operator_divide", &compileDivide); - engine->addCompileFunction(this, "operator_random", &compileRandom); - engine->addCompileFunction(this, "operator_lt", &compileLt); - engine->addCompileFunction(this, "operator_equals", &compileEquals); - engine->addCompileFunction(this, "operator_gt", &compileGt); - engine->addCompileFunction(this, "operator_and", &compileAnd); - engine->addCompileFunction(this, "operator_or", &compileOr); - engine->addCompileFunction(this, "operator_not", &compileNot); - engine->addCompileFunction(this, "operator_join", &compileJoin); - engine->addCompileFunction(this, "operator_letter_of", &compileLetterOf); - engine->addCompileFunction(this, "operator_length", &compileLength); - engine->addCompileFunction(this, "operator_contains", &compileContains); - engine->addCompileFunction(this, "operator_mod", &compileMod); - engine->addCompileFunction(this, "operator_round", &compileRound); - engine->addCompileFunction(this, "operator_mathop", &compileMathOp); -} - -CompilerValue *OperatorBlocks::compileAdd(Compiler *compiler) -{ - return compiler->createAdd(compiler->addInput("NUM1"), compiler->addInput("NUM2")); -} - -CompilerValue *OperatorBlocks::compileSubtract(Compiler *compiler) -{ - return compiler->createSub(compiler->addInput("NUM1"), compiler->addInput("NUM2")); -} - -CompilerValue *OperatorBlocks::compileMultiply(Compiler *compiler) -{ - return compiler->createMul(compiler->addInput("NUM1"), compiler->addInput("NUM2")); -} - -CompilerValue *OperatorBlocks::compileDivide(Compiler *compiler) -{ - return compiler->createDiv(compiler->addInput("NUM1"), compiler->addInput("NUM2")); -} - -CompilerValue *OperatorBlocks::compileRandom(Compiler *compiler) -{ - auto from = compiler->addInput("FROM"); - auto to = compiler->addInput("TO"); - return compiler->createRandom(from, to); -} - -CompilerValue *OperatorBlocks::compileLt(Compiler *compiler) -{ - return compiler->createCmpLT(compiler->addInput("OPERAND1"), compiler->addInput("OPERAND2")); -} - -CompilerValue *OperatorBlocks::compileEquals(Compiler *compiler) -{ - return compiler->createCmpEQ(compiler->addInput("OPERAND1"), compiler->addInput("OPERAND2")); -} - -CompilerValue *OperatorBlocks::compileGt(Compiler *compiler) -{ - return compiler->createCmpGT(compiler->addInput("OPERAND1"), compiler->addInput("OPERAND2")); -} - -CompilerValue *OperatorBlocks::compileAnd(Compiler *compiler) -{ - return compiler->createAnd(compiler->addInput("OPERAND1"), compiler->addInput("OPERAND2")); -} - -CompilerValue *OperatorBlocks::compileOr(Compiler *compiler) -{ - return compiler->createOr(compiler->addInput("OPERAND1"), compiler->addInput("OPERAND2")); -} - -CompilerValue *OperatorBlocks::compileNot(Compiler *compiler) -{ - return compiler->createNot(compiler->addInput("OPERAND")); -} - -CompilerValue *OperatorBlocks::compileJoin(Compiler *compiler) -{ - auto string1 = compiler->addInput("STRING1"); - auto string2 = compiler->addInput("STRING2"); - return compiler->addFunctionCall("operator_join", Compiler::StaticType::String, { Compiler::StaticType::String, Compiler::StaticType::String }, { string1, string2 }); -} - -CompilerValue *OperatorBlocks::compileLetterOf(Compiler *compiler) -{ - auto letter = compiler->addInput("LETTER"); - auto string = compiler->addInput("STRING"); - return compiler->addFunctionCall("operator_letter_of", Compiler::StaticType::String, { Compiler::StaticType::Number, Compiler::StaticType::String }, { letter, string }); -} - -CompilerValue *OperatorBlocks::compileLength(Compiler *compiler) -{ - auto string = compiler->addInput("STRING"); - return compiler->addFunctionCall("operator_length", Compiler::StaticType::Number, { Compiler::StaticType::String }, { string }); -} - -CompilerValue *OperatorBlocks::compileContains(Compiler *compiler) -{ - auto string1 = compiler->addInput("STRING1"); - auto string2 = compiler->addInput("STRING2"); - return compiler->addFunctionCall("operator_contains", Compiler::StaticType::Bool, { Compiler::StaticType::String, Compiler::StaticType::String }, { string1, string2 }); -} - -CompilerValue *OperatorBlocks::compileMod(Compiler *compiler) -{ - return compiler->createMod(compiler->addInput("NUM1"), compiler->addInput("NUM2")); -} - -CompilerValue *OperatorBlocks::compileRound(Compiler *compiler) -{ - return compiler->createRound(compiler->addInput("NUM")); -} - -CompilerValue *OperatorBlocks::compileMathOp(Compiler *compiler) -{ - Field *opField = compiler->field("OPERATOR"); - const std::string numInput = "NUM"; - const std::string &op = opField->value().toString(); - - if (op == "abs") - return compiler->createAbs(compiler->addInput(numInput)); - else if (op == "floor") - return compiler->createFloor(compiler->addInput(numInput)); - else if (op == "ceiling") - return compiler->createCeil(compiler->addInput(numInput)); - else if (op == "sqrt") - return compiler->createSqrt(compiler->addInput(numInput)); - else if (op == "sin") - return compiler->createSin(compiler->addInput(numInput)); - else if (op == "cos") - return compiler->createCos(compiler->addInput(numInput)); - else if (op == "tan") - return compiler->createTan(compiler->addInput(numInput)); - else if (op == "asin") - return compiler->createAsin(compiler->addInput(numInput)); - else if (op == "acos") - return compiler->createAcos(compiler->addInput(numInput)); - else if (op == "atan") - return compiler->createAtan(compiler->addInput(numInput)); - else if (op == "ln") - return compiler->createLn(compiler->addInput(numInput)); - else if (op == "log") - return compiler->createLog10(compiler->addInput(numInput)); - else if (op == "e ^") - return compiler->createExp(compiler->addInput(numInput)); - else if (op == "10 ^") - return compiler->createExp10(compiler->addInput(numInput)); - else - return compiler->addConstValue(Value()); -} - -extern "C" char *operator_join(const char *string1, const char *string2) -{ - const size_t len1 = strlen(string1); - const size_t len2 = strlen(string2); - - char *ret = (char *)malloc((len1 + len2 + 1) * sizeof(char)); - size_t i; - - for (i = 0; i < len1; i++) - ret[i] = string1[i]; - - for (i = 0; i < len2 + 1; i++) // +1: null-terminate - ret[len1 + i] = string2[i]; - - return ret; -} - -extern "C" char *operator_letter_of(double letter, const char *string) -{ - const size_t len = strlen(string); - - if (letter < 1 || letter > len) { - char *ret = (char *)malloc(sizeof(char)); - ret[0] = '\0'; - return ret; - } - - // TODO: Rewrite this - std::u16string u16 = utf8::utf8to16(std::string(string)); - std::string str = utf8::utf16to8(std::u16string({ u16[(size_t)letter - 1] })); - char *ret = (char *)malloc((str.size() + 1) * sizeof(char)); - strcpy(ret, str.c_str()); - - return ret; -} - -extern "C" double operator_length(const char *string) -{ - // TODO: Rewrite this - return utf8::utf8to16(std::string(string)).size(); -} - -extern "C" bool operator_contains(const char *string1, const char *string2) -{ - // TODO: Rewrite this - std::u16string u16string1 = utf8::utf8to16(std::string(string1)); - std::u16string u16string2 = utf8::utf8to16(std::string(string2)); - std::transform(u16string1.begin(), u16string1.end(), u16string1.begin(), ::tolower); - std::transform(u16string2.begin(), u16string2.end(), u16string2.begin(), ::tolower); - return (u16string1.find(u16string2) != std::u16string::npos); -} diff --git a/src/dev/blocks/operatorblocks.h b/src/dev/blocks/operatorblocks.h deleted file mode 100644 index 1804e931..00000000 --- a/src/dev/blocks/operatorblocks.h +++ /dev/null @@ -1,41 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 - -#pragma once - -#include - -namespace libscratchcpp -{ - -class IRandomGenerator; - -class OperatorBlocks : public IExtension -{ - public: - std::string name() const override; - std::string description() const override; - - void registerBlocks(IEngine *engine) override; - - private: - static CompilerValue *compileAdd(Compiler *compiler); - static CompilerValue *compileSubtract(Compiler *compiler); - static CompilerValue *compileMultiply(Compiler *compiler); - static CompilerValue *compileDivide(Compiler *compiler); - static CompilerValue *compileRandom(Compiler *compiler); - static CompilerValue *compileLt(Compiler *compiler); - static CompilerValue *compileEquals(Compiler *compiler); - static CompilerValue *compileGt(Compiler *compiler); - static CompilerValue *compileAnd(Compiler *compiler); - static CompilerValue *compileOr(Compiler *compiler); - static CompilerValue *compileNot(Compiler *compiler); - static CompilerValue *compileJoin(Compiler *compiler); - static CompilerValue *compileLetterOf(Compiler *compiler); - static CompilerValue *compileLength(Compiler *compiler); - static CompilerValue *compileContains(Compiler *compiler); - static CompilerValue *compileMod(Compiler *compiler); - static CompilerValue *compileRound(Compiler *compiler); - static CompilerValue *compileMathOp(Compiler *compiler); -}; - -} // namespace libscratchcpp diff --git a/src/dev/blocks/sensingblocks.cpp b/src/dev/blocks/sensingblocks.cpp deleted file mode 100644 index 42896ecb..00000000 --- a/src/dev/blocks/sensingblocks.cpp +++ /dev/null @@ -1,19 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 - -#include "sensingblocks.h" - -using namespace libscratchcpp; - -std::string SensingBlocks::name() const -{ - return "Sensing"; -} - -std::string SensingBlocks::description() const -{ - return name() + " blocks"; -} - -void SensingBlocks::registerBlocks(IEngine *engine) -{ -} diff --git a/src/dev/blocks/sensingblocks.h b/src/dev/blocks/sensingblocks.h deleted file mode 100644 index ab934fb0..00000000 --- a/src/dev/blocks/sensingblocks.h +++ /dev/null @@ -1,19 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 - -#pragma once - -#include - -namespace libscratchcpp -{ - -class SensingBlocks : public IExtension -{ - public: - std::string name() const override; - std::string description() const override; - - void registerBlocks(IEngine *engine) override; -}; - -} // namespace libscratchcpp diff --git a/src/dev/blocks/soundblocks.cpp b/src/dev/blocks/soundblocks.cpp deleted file mode 100644 index 4a440a9a..00000000 --- a/src/dev/blocks/soundblocks.cpp +++ /dev/null @@ -1,19 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 - -#include "soundblocks.h" - -using namespace libscratchcpp; - -std::string SoundBlocks::name() const -{ - return "Sound"; -} - -std::string SoundBlocks::description() const -{ - return name() + " blocks"; -} - -void SoundBlocks::registerBlocks(IEngine *engine) -{ -} diff --git a/src/dev/blocks/soundblocks.h b/src/dev/blocks/soundblocks.h deleted file mode 100644 index afae6c9c..00000000 --- a/src/dev/blocks/soundblocks.h +++ /dev/null @@ -1,19 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 - -#pragma once - -#include - -namespace libscratchcpp -{ - -class SoundBlocks : public IExtension -{ - public: - std::string name() const override; - std::string description() const override; - - void registerBlocks(IEngine *engine) override; -}; - -} // namespace libscratchcpp diff --git a/src/dev/blocks/variableblocks.cpp b/src/dev/blocks/variableblocks.cpp deleted file mode 100644 index 177d8dab..00000000 --- a/src/dev/blocks/variableblocks.cpp +++ /dev/null @@ -1,67 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 - -#include -#include -#include -#include -#include -#include - -#include "variableblocks.h" - -using namespace libscratchcpp; - -std::string VariableBlocks::name() const -{ - return "Variables"; -} - -std::string VariableBlocks::description() const -{ - return "Variable blocks"; -} - -void VariableBlocks::registerBlocks(IEngine *engine) -{ - engine->addCompileFunction(this, "data_variable", &compileVariable); - engine->addCompileFunction(this, "data_setvariableto", &compileSetVariableTo); - engine->addCompileFunction(this, "data_changevariableby", &compileChangeVariableBy); -} - -CompilerValue *VariableBlocks::compileVariable(Compiler *compiler) -{ - Field *varField = compiler->field("VARIABLE"); - Variable *var = static_cast(varField->valuePtr().get()); - assert(var); - - if (var) - return compiler->addVariableValue(var); - else - return compiler->addConstValue(Value()); -} - -CompilerValue *VariableBlocks::compileSetVariableTo(Compiler *compiler) -{ - Field *varField = compiler->field("VARIABLE"); - Variable *var = static_cast(varField->valuePtr().get()); - assert(var); - - if (var) - compiler->createVariableWrite(var, compiler->addInput("VALUE")); - - return nullptr; -} - -CompilerValue *VariableBlocks::compileChangeVariableBy(Compiler *compiler) -{ - Field *varField = compiler->field("VARIABLE"); - Variable *var = static_cast(varField->valuePtr().get()); - assert(var); - - if (var) { - CompilerValue *value = compiler->createAdd(compiler->addVariableValue(var), compiler->addInput("VALUE")); - compiler->createVariableWrite(var, value); - } - - return nullptr; -} diff --git a/src/dev/blocks/variableblocks.h b/src/dev/blocks/variableblocks.h deleted file mode 100644 index 9cb0bcc4..00000000 --- a/src/dev/blocks/variableblocks.h +++ /dev/null @@ -1,24 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 - -#pragma once - -#include - -namespace libscratchcpp -{ - -class VariableBlocks : public IExtension -{ - public: - std::string name() const override; - std::string description() const override; - - void registerBlocks(IEngine *engine) override; - - private: - static CompilerValue *compileVariable(Compiler *compiler); - static CompilerValue *compileSetVariableTo(Compiler *compiler); - static CompilerValue *compileChangeVariableBy(Compiler *compiler); -}; - -} // namespace libscratchcpp diff --git a/src/dev/engine/CMakeLists.txt b/src/dev/engine/CMakeLists.txt deleted file mode 100644 index be71b580..00000000 --- a/src/dev/engine/CMakeLists.txt +++ /dev/null @@ -1,30 +0,0 @@ -target_sources(scratchcpp - PRIVATE - compiler.cpp - compiler_p.cpp - compiler_p.h - compilercontext.cpp - compilercontext_p.cpp - compilercontext_p.h - compilervalue.cpp - compilervalue_p.cpp - compilervalue_p.h - compilerconstant.cpp - compilerconstant_p.cpp - compilerconstant_p.h - compilerlocalvariable.cpp - compilerlocalvariable_p.cpp - compilerlocalvariable_p.h - executioncontext.cpp - executioncontext_p.cpp - executioncontext_p.h - promise.cpp - promise_p.cpp - promise_p.h - internal/icodebuilder.h - internal/icodebuilderfactory.h - internal/codebuilderfactory.cpp - internal/codebuilderfactory.h -) - -add_subdirectory(internal/llvm) diff --git a/src/dev/engine/compiler.cpp b/src/dev/engine/compiler.cpp deleted file mode 100644 index 23fc0045..00000000 --- a/src/dev/engine/compiler.cpp +++ /dev/null @@ -1,682 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 - -#include -#include -#include -#include -#include -#include - -#include "compiler_p.h" -#include "internal/icodebuilderfactory.h" -#include "internal/icodebuilder.h" - -using namespace libscratchcpp; - -/*! Constructs Compiler using the given context. */ -Compiler::Compiler(CompilerContext *ctx) : - impl(spimpl::make_unique_impl(ctx)) -{ -} - -/*! Constructs Compiler using a new context for the given target. */ -Compiler::Compiler(IEngine *engine, Target *target) : - impl(spimpl::make_unique_impl(engine, target)) -{ -} - -/*! Returns the Engine of the project. */ -IEngine *Compiler::engine() const -{ - return impl->ctx->engine(); -} - -/*! Returns the Target of this compiler. */ -Target *Compiler::target() const -{ - return impl->ctx->target(); -} - -/*! Returns currently compiled block. */ -std::shared_ptr Compiler::block() const -{ - return impl->block; -} - -/*! Compiles the script starting with the given block. */ -std::shared_ptr Compiler::compile(std::shared_ptr startBlock) -{ - BlockPrototype *procedurePrototype = nullptr; - - if (startBlock) { - // TODO: Move procedure definition logic to the custom blocks extension - auto input = startBlock->inputAt(0); - - if (input && input->valueBlock()) { - procedurePrototype = input->valueBlock()->mutationPrototype(); - - if (procedurePrototype && procedurePrototype->procCode().empty()) - procedurePrototype = nullptr; - } - } - - impl->builder = impl->builderFactory->create(impl->ctx, procedurePrototype); - impl->substackTree.clear(); - impl->substackHit = false; - impl->emptySubstack = false; - impl->warp = false; - impl->block = startBlock; - - while (impl->block) { - if (impl->block->compileFunction()) { - assert(impl->customIfStatementCount == 0); - impl->block->compile(this); - - if (impl->customIfStatementCount > 0) { - std::cerr << "error: if statement created by block '" << impl->block->opcode() << "' not terminated" << std::endl; - assert(false); - } - - if (impl->emptySubstack) { - impl->emptySubstack = false; - impl->substackEnd(); - } - - if (impl->customLoopCount > 0) { - std::cerr << "error: loop created by block '" << impl->block->opcode() << "' not terminated" << std::endl; - assert(false); - } - } else { - std::cout << "warning: unsupported block: " << impl->block->opcode() << std::endl; - impl->unsupportedBlocks.insert(impl->block->opcode()); - } - - if (impl->substackHit) { - impl->substackHit = false; - continue; - } - - if (impl->block) - impl->block = impl->block->next(); - - if (!impl->block && !impl->substackTree.empty()) - impl->substackEnd(); - } - - return impl->builder->finalize(); -} - -/*! - * Optimizes all compiled scripts before they're called for the first time. - * \note Call this only after compiling all scripts. - */ -void Compiler::preoptimize() -{ - impl->ctx->preoptimize(); -} - -/*! - * Adds a call to the given function.\n - * For example: extern "C" bool some_block(double arg1, const char *arg2) - */ -CompilerValue *Compiler::addFunctionCall(const std::string &functionName, StaticType returnType, const ArgTypes &argTypes, const Args &args) -{ - assert(argTypes.size() == args.size()); - return impl->builder->addFunctionCall(functionName, returnType, argTypes, args); -} - -/*! - * Adds a call to the given function with a target parameter.\n - * For example: extern "C" bool some_block(Target *target, double arg1, const char *arg2) - */ -CompilerValue *Compiler::addTargetFunctionCall(const std::string &functionName, StaticType returnType, const ArgTypes &argTypes, const Args &args) -{ - assert(argTypes.size() == args.size()); - return impl->builder->addTargetFunctionCall(functionName, returnType, argTypes, args); -} - -/*! - * Adds a call to the given function with an execution context parameter.\n - * For example: extern "C" bool some_block(ExecutionContext *ctx, double arg1, const char *arg2) - */ -CompilerValue *Compiler::addFunctionCallWithCtx(const std::string &functionName, StaticType returnType, const ArgTypes &argTypes, const Args &args) -{ - assert(argTypes.size() == args.size()); - return impl->builder->addFunctionCallWithCtx(functionName, returnType, argTypes, args); -} - -/*! Adds the given constant to the compiled code. */ -CompilerConstant *Compiler::addConstValue(const Value &value) -{ - return static_cast(impl->builder->addConstValue(value)); -} - -/*! Adds the index of the current repeat loop to the compiled code. */ -CompilerValue *Compiler::addLoopIndex() -{ - return impl->builder->addLoopIndex(); -} - -/*! Adds the value of the given local variable to the code. */ -CompilerValue *Compiler::addLocalVariableValue(CompilerLocalVariable *variable) -{ - return impl->builder->addLocalVariableValue(variable); -} - -/*! Adds the value of the given variable to the code. */ -CompilerValue *Compiler::addVariableValue(Variable *variable) -{ - return impl->builder->addVariableValue(variable); -} - -/*! Adds the string representation of the given list to the code. */ -CompilerValue *Compiler::addListContents(List *list) -{ - return impl->builder->addListContents(list); -} - -/*! Adds the item with index of the given list to the code. */ -CompilerValue *Compiler::addListItem(List *list, CompilerValue *index) -{ - return impl->builder->addListItem(list, index); -} - -/*! Adds the index of item of the given list to the code. */ -CompilerValue *Compiler::addListItemIndex(List *list, CompilerValue *item) -{ - return impl->builder->addListItemIndex(list, item); -} - -/*! Adds the result of a list contains item check to the code. */ -CompilerValue *Compiler::addListContains(List *list, CompilerValue *item) -{ - return impl->builder->addListContains(list, item); -} - -/*! Adds the length of the given list to the code. */ -CompilerValue *Compiler::addListSize(List *list) -{ - return impl->builder->addListSize(list); -} - -/*! Adds the procedure argument with the given name to the code. */ -CompilerValue *Compiler::addProcedureArgument(const std::string &name) -{ - return impl->builder->addProcedureArgument(name); -} - -/*! Compiles the given input (resolved by name) and adds it to the compiled code. */ -CompilerValue *Compiler::addInput(const std::string &name) -{ - return addInput(impl->block->inputAt(impl->block->findInput(name)).get()); -} - -/*! Compiles the given input and adds it to the compiled code. */ -CompilerValue *Compiler::addInput(Input *input) -{ - if (!input) - return addConstValue(Value()); - - switch (input->type()) { - case Input::Type::Shadow: - case Input::Type::NoShadow: { - if (input->pointsToDropdownMenu()) - return addConstValue(input->selectedMenuItem()); - else { - CompilerValue *ret = nullptr; - auto previousBlock = impl->block; - impl->block = input->valueBlock(); - - if (impl->block) { - if (impl->block->compileFunction()) - ret = impl->block->compile(this); - else { - std::cout << "warning: unsupported reporter block: " << impl->block->opcode() << std::endl; - impl->unsupportedBlocks.insert(impl->block->opcode()); - ret = addConstValue(Value()); - } - } else - ret = addConstValue(input->primaryValue()->value()); - - impl->block = previousBlock; - return ret; - } - } - - case Input::Type::ObscuredShadow: { - CompilerValue *ret = nullptr; - auto previousBlock = impl->block; - impl->block = input->valueBlock(); - - if (impl->block) { - if (impl->block->compileFunction()) - ret = impl->block->compile(this); - else { - std::cout << "warning: unsupported reporter block: " << impl->block->opcode() << std::endl; - impl->unsupportedBlocks.insert(impl->block->opcode()); - ret = addConstValue(Value()); - } - } else - ret = input->primaryValue()->compile(this); - - impl->block = previousBlock; - return ret; - } - } - - return nullptr; -} - -/*! Creates an add instruction. */ -CompilerValue *Compiler::createAdd(CompilerValue *operand1, CompilerValue *operand2) -{ - return impl->builder->createAdd(operand1, operand2); -} - -/*! Creates a subtract instruction. */ -CompilerValue *Compiler::createSub(CompilerValue *operand1, CompilerValue *operand2) -{ - return impl->builder->createSub(operand1, operand2); -} - -/*! Creates a multiply instruction. */ -CompilerValue *Compiler::createMul(CompilerValue *operand1, CompilerValue *operand2) -{ - return impl->builder->createMul(operand1, operand2); -} - -/*! Creates a divide instruction. */ -CompilerValue *Compiler::createDiv(CompilerValue *operand1, CompilerValue *operand2) -{ - return impl->builder->createDiv(operand1, operand2); -} - -/*! Creates a random instruction (Scratch behavior). */ -CompilerValue *Compiler::createRandom(CompilerValue *from, CompilerValue *to) -{ - return impl->builder->createRandom(from, to); -} - -/*! - * Creates a random integer instruction. - * \note Infinity or NaN results in undefined behavior. - */ -CompilerValue *Compiler::createRandomInt(CompilerValue *from, CompilerValue *to) -{ - return impl->builder->createRandomInt(from, to); -} - -/*! Creates an equality comparison instruction. */ -CompilerValue *Compiler::createCmpEQ(CompilerValue *operand1, CompilerValue *operand2) -{ - return impl->builder->createCmpEQ(operand1, operand2); -} - -/*! Creates a greater than comparison instruction. */ -CompilerValue *Compiler::createCmpGT(CompilerValue *operand1, CompilerValue *operand2) -{ - return impl->builder->createCmpGT(operand1, operand2); -} - -/*! Creates a lower than comparison instruction. */ -CompilerValue *Compiler::createCmpLT(CompilerValue *operand1, CompilerValue *operand2) -{ - return impl->builder->createCmpLT(operand1, operand2); -} - -/*! Creates a string equality comparison (explicitly casts operands to string). */ -CompilerValue *Compiler::createStrCmpEQ(CompilerValue *string1, CompilerValue *string2, bool caseSensitive) -{ - return impl->builder->createStrCmpEQ(string1, string2, caseSensitive); -} - -/*! Creates an AND operation. */ -CompilerValue *Compiler::createAnd(CompilerValue *operand1, CompilerValue *operand2) -{ - return impl->builder->createAnd(operand1, operand2); -} - -/*! Creates an OR operation. */ -CompilerValue *Compiler::createOr(CompilerValue *operand1, CompilerValue *operand2) -{ - return impl->builder->createOr(operand1, operand2); -} - -/*! Creates a NOT operation. */ -CompilerValue *Compiler::createNot(CompilerValue *operand) -{ - return impl->builder->createNot(operand); -} - -/*! Creates a remainder operation. */ -CompilerValue *Compiler::createMod(CompilerValue *num1, CompilerValue *num2) -{ - return impl->builder->createMod(num1, num2); -} - -/*! Creates a round operation. */ -CompilerValue *Compiler::createRound(CompilerValue *num) -{ - return impl->builder->createRound(num); -} - -/*! Creates an abs operation. */ -CompilerValue *Compiler::createAbs(CompilerValue *num) -{ - return impl->builder->createAbs(num); -} - -/*! Creates a floor operation. */ -CompilerValue *Compiler::createFloor(CompilerValue *num) -{ - return impl->builder->createFloor(num); -} - -/*! Creates a ceiling operation. */ -CompilerValue *Compiler::createCeil(CompilerValue *num) -{ - return impl->builder->createCeil(num); -} - -/*! Creates a square root operation. */ -CompilerValue *Compiler::createSqrt(CompilerValue *num) -{ - return impl->builder->createSqrt(num); -} - -/*! Creates a sin operation. */ -CompilerValue *Compiler::createSin(CompilerValue *num) -{ - return impl->builder->createSin(num); -} - -/*! Creates a cos operation. */ -CompilerValue *Compiler::createCos(CompilerValue *num) -{ - return impl->builder->createCos(num); -} - -/*! Creates a tan operation. */ -CompilerValue *Compiler::createTan(CompilerValue *num) -{ - return impl->builder->createTan(num); -} - -/*! Creates an asin operation. */ -CompilerValue *Compiler::createAsin(CompilerValue *num) -{ - return impl->builder->createAsin(num); -} - -/*! Creates an acos operation. */ -CompilerValue *Compiler::createAcos(CompilerValue *num) -{ - return impl->builder->createAcos(num); -} - -/*! Creates an atan operation. */ -CompilerValue *Compiler::createAtan(CompilerValue *num) -{ - return impl->builder->createAtan(num); -} - -/*! Creates an ln operation. */ -CompilerValue *Compiler::createLn(CompilerValue *num) -{ - return impl->builder->createLn(num); -} - -/*! Creates a log10 operation. */ -CompilerValue *Compiler::createLog10(CompilerValue *num) -{ - return impl->builder->createLog10(num); -} - -/*! Creates an e^x operation. */ -CompilerValue *Compiler::createExp(CompilerValue *num) -{ - return impl->builder->createExp(num); -} - -/*! Creates a 10^x operation. */ -CompilerValue *Compiler::createExp10(CompilerValue *num) -{ - return impl->builder->createExp10(num); -} - -/*! Creates a select instruction (ternary operator). */ -CompilerValue *Compiler::createSelect(CompilerValue *cond, CompilerValue *trueValue, CompilerValue *falseValue, StaticType valueType) -{ - return impl->builder->createSelect(cond, trueValue, falseValue, valueType); -} - -/*! Creates a local variable with the given type. */ -CompilerLocalVariable *Compiler::createLocalVariable(StaticType type) -{ - return impl->builder->createLocalVariable(type); -} - -/*! Creates a local variable write operation. */ -void Compiler::createLocalVariableWrite(CompilerLocalVariable *variable, CompilerValue *value) -{ - impl->builder->createLocalVariableWrite(variable, value); -} - -/*! Creates a variable write operation. */ -void Compiler::createVariableWrite(Variable *variable, CompilerValue *value) -{ - impl->builder->createVariableWrite(variable, value); -} - -/*! Creates a clear list operation. */ -void Compiler::createListClear(List *list) -{ - impl->builder->createListClear(list); -} - -/*! - * Creates a remove item from list operation. - * \note The index starts with 0 and is cast to number, special strings like "last" are not handled. - */ -void Compiler::createListRemove(List *list, CompilerValue *index) -{ - impl->builder->createListRemove(list, index); -} - -/*! Creates a list append operation. */ -void Compiler::createListAppend(List *list, CompilerValue *item) -{ - impl->builder->createListAppend(list, item); -} - -/*! Creates a list insert operation. */ -void Compiler::createListInsert(List *list, CompilerValue *index, CompilerValue *item) -{ - impl->builder->createListInsert(list, index, item); -} - -/*! Creates a list replace operation. */ -void Compiler::createListReplace(List *list, CompilerValue *index, CompilerValue *item) -{ - impl->builder->createListReplace(list, index, item); -} - -/*! - * Starts a custom if statement. - * \note The if statement must be terminated using endIf() after compiling your block. - */ -void Compiler::beginIfStatement(CompilerValue *cond) -{ - impl->builder->beginIfStatement(cond); - impl->customIfStatementCount++; -} - -/*! Starts the else branch of custom if statement. */ -void Compiler::beginElseBranch() -{ - impl->builder->beginElseBranch(); -} - -/*! Ends custom if statement. */ -void Compiler::endIf() -{ - if (impl->customIfStatementCount == 0) { - std::cerr << "error: called Compiler::endIf() without an if statement"; - assert(false); - return; - } - - impl->builder->endIf(); - impl->customIfStatementCount--; -} - -/*! - * Begins a custom while loop. - * \note The loop must be terminated with endLoop() after compiling your block. - */ -void Compiler::beginWhileLoop(CompilerValue *cond) -{ - impl->builder->beginWhileLoop(cond); - impl->customLoopCount++; -} - -/*! - * Begins a custom repeat until loop. - * \note The loop must be terminated with endLoop() after compiling your block. - */ -void Compiler::beginRepeatUntilLoop(CompilerValue *cond) -{ - impl->builder->beginRepeatUntilLoop(cond); - impl->customLoopCount++; -} - -/*! Begins a while/until loop condition. */ -void Compiler::beginLoopCondition() -{ - impl->builder->beginLoopCondition(); -} - -/*! Ends custom loop. */ -void Compiler::endLoop() -{ - if (impl->customLoopCount == 0) { - std::cerr << "error: called Compiler::endLoop() without a loop"; - assert(false); - return; - } - - impl->builder->endLoop(); - impl->customLoopCount--; -} - -/*! Jumps to the given if substack. */ -void Compiler::moveToIf(CompilerValue *cond, std::shared_ptr substack) -{ - if (!substack) - return; // ignore empty if statements - - impl->substackHit = true; - impl->substackTree.push_back({ { impl->block, nullptr }, CompilerPrivate::SubstackType::IfStatement }); - impl->block = substack; - impl->builder->beginIfStatement(cond); -} - -/*! Jumps to the given if/else substack. The second substack is used for the else branch. */ -void Compiler::moveToIfElse(CompilerValue *cond, std::shared_ptr substack1, std::shared_ptr substack2) -{ - if (!substack1 && !substack2) - return; // ignore empty if statements - - impl->substackHit = true; - impl->substackTree.push_back({ { impl->block, substack2 }, CompilerPrivate::SubstackType::IfStatement }); - impl->block = substack1; - impl->builder->beginIfStatement(cond); - - if (!impl->block) - impl->emptySubstack = true; -} - -/*! Jumps to the given repeat loop substack. */ -void Compiler::moveToRepeatLoop(CompilerValue *count, std::shared_ptr substack) -{ - impl->substackHit = true; - impl->substackTree.push_back({ { impl->block, nullptr }, CompilerPrivate::SubstackType::Loop }); - impl->block = substack; - impl->builder->beginRepeatLoop(count); - - if (!impl->block) - impl->emptySubstack = true; -} - -/*! Jumps to the given while loop substack. */ -void Compiler::moveToWhileLoop(CompilerValue *cond, std::shared_ptr substack) -{ - impl->substackHit = true; - impl->substackTree.push_back({ { impl->block, nullptr }, CompilerPrivate::SubstackType::Loop }); - impl->block = substack; - impl->builder->beginWhileLoop(cond); - - if (!impl->block) - impl->emptySubstack = true; -} - -/*! Jumps to the given until loop substack. */ -void Compiler::moveToRepeatUntilLoop(CompilerValue *cond, std::shared_ptr substack) -{ - impl->substackHit = true; - impl->substackTree.push_back({ { impl->block, nullptr }, CompilerPrivate::SubstackType::Loop }); - impl->block = substack; - impl->builder->beginRepeatUntilLoop(cond); - - if (!impl->block) - impl->emptySubstack = true; -} - -/*! Makes current script run without screen refresh. */ -void Compiler::warp() -{ - impl->warp = true; -} - -/*! Creates a suspend instruction. */ -void Compiler::createYield() -{ - impl->builder->yield(); -} - -/*! Creates a stop script instruction. */ -void Compiler::createStop() -{ - impl->builder->createStop(); -} - -/*! Creates a call to the procedure with the given prototype. */ -void Compiler::createProcedureCall(BlockPrototype *prototype, const libscratchcpp::Compiler::Args &args) -{ - impl->builder->createProcedureCall(prototype, args); -} - -/*! Convenience method which returns the field with the given name. */ -Input *Compiler::input(const std::string &name) const -{ - return impl->block->inputAt(impl->block->findInput(name)).get(); -} - -/*! Convenience method which returns the field with the given name. */ -Field *Compiler::field(const std::string &name) const -{ - return impl->block->fieldAt(impl->block->findField(name)).get(); -} - -/*! Returns unsupported block opcodes which were found when compiling. */ -const std::unordered_set &Compiler::unsupportedBlocks() const -{ - return impl->unsupportedBlocks; -} - -/*! Creates a compiler context for the given target. */ -std::shared_ptr Compiler::createContext(IEngine *engine, Target *target) -{ - CompilerPrivate::initBuilderFactory(); - return CompilerPrivate::builderFactory->createCtx(engine, target); -} diff --git a/src/dev/engine/compiler_p.cpp b/src/dev/engine/compiler_p.cpp deleted file mode 100644 index 963c0d3e..00000000 --- a/src/dev/engine/compiler_p.cpp +++ /dev/null @@ -1,69 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 - -#include -#include - -#include "compiler_p.h" - -#include "internal/codebuilderfactory.h" -#include "internal/icodebuilder.h" - -using namespace libscratchcpp; - -CompilerPrivate::CompilerPrivate(CompilerContext *ctx) : - ctx(ctx) -{ - assert(ctx); - initBuilderFactory(); -} - -CompilerPrivate::CompilerPrivate(IEngine *engine, Target *target) : - ctxPtr(Compiler::createContext(engine, target)), - ctx(ctxPtr.get()) -{ - initBuilderFactory(); -} - -void CompilerPrivate::initBuilderFactory() -{ - if (!builderFactory) - builderFactory = CodeBuilderFactory::instance().get(); -} - -void CompilerPrivate::substackEnd() -{ - auto &parent = substackTree.back(); - - switch (parent.second) { - case SubstackType::Loop: - // Yield at loop end if not running without screen refresh - /*if (!warp) - builder->yield();*/ - - builder->endLoop(); - break; - - case SubstackType::IfStatement: - if (parent.first.second) { - builder->beginElseBranch(); - block = parent.first.second; - parent.first.second = nullptr; - return; - } else - builder->endIf(); - - break; - } - - auto parentBlock = parent.first.first; - - if (parentBlock) - block = parentBlock->next(); - else - block = nullptr; - - substackTree.pop_back(); - - if (!block && !substackTree.empty()) - substackEnd(); -} diff --git a/src/dev/engine/compiler_p.h b/src/dev/engine/compiler_p.h deleted file mode 100644 index 593accbf..00000000 --- a/src/dev/engine/compiler_p.h +++ /dev/null @@ -1,52 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 - -#pragma once - -#include -#include -#include - -namespace libscratchcpp -{ - -class CompilerContext; -class IEngine; -class Target; -class Block; -class CompilerValue; -class ICodeBuilderFactory; -class ICodeBuilder; - -struct CompilerPrivate -{ - enum class SubstackType - { - Loop, - IfStatement - }; - - CompilerPrivate(CompilerContext *ctx); - CompilerPrivate(IEngine *engine, Target *target); - - static void initBuilderFactory(); - - void substackEnd(); - - std::shared_ptr ctxPtr; // for self-managed contexts - CompilerContext *ctx = nullptr; - - std::shared_ptr block; - int customIfStatementCount = 0; - int customLoopCount = 0; - std::vector, std::shared_ptr>, SubstackType>> substackTree; - bool substackHit = false; - bool emptySubstack = false; - bool warp = false; - - static inline ICodeBuilderFactory *builderFactory = nullptr; - std::shared_ptr builder; - - std::unordered_set unsupportedBlocks; -}; - -} // namespace libscratchcpp diff --git a/src/engine/CMakeLists.txt b/src/engine/CMakeLists.txt index d1b47737..d4aa8ed5 100644 --- a/src/engine/CMakeLists.txt +++ b/src/engine/CMakeLists.txt @@ -1,8 +1,26 @@ target_sources(scratchcpp PRIVATE - virtualmachine.cpp - virtualmachine_p.cpp - virtualmachine_p.h + compiler.cpp + compiler_p.cpp + compiler_p.h + compilercontext.cpp + compilercontext_p.cpp + compilercontext_p.h + compilervalue.cpp + compilervalue_p.cpp + compilervalue_p.h + compilerconstant.cpp + compilerconstant_p.cpp + compilerconstant_p.h + compilerlocalvariable.cpp + compilerlocalvariable_p.cpp + compilerlocalvariable_p.h + executioncontext.cpp + executioncontext_p.cpp + executioncontext_p.h + promise.cpp + promise_p.cpp + promise_p.h script.cpp script_p.cpp script_p.h @@ -11,6 +29,10 @@ target_sources(scratchcpp thread_p.h internal/engine.cpp internal/engine.h + internal/icodebuilder.h + internal/icodebuilderfactory.h + internal/codebuilderfactory.cpp + internal/codebuilderfactory.h internal/clock.cpp internal/clock.h internal/iclock.h @@ -22,11 +44,4 @@ target_sources(scratchcpp internal/randomgenerator.cpp ) -if(NOT LIBSCRATCHCPP_USE_LLVM) - target_sources(scratchcpp - PRIVATE - compiler.cpp - compiler_p.cpp - compiler_p.h - ) -endif() +add_subdirectory(internal/llvm) diff --git a/src/engine/compiler.cpp b/src/engine/compiler.cpp index 2ff823ba..b49ed779 100644 --- a/src/engine/compiler.cpp +++ b/src/engine/compiler.cpp @@ -1,78 +1,100 @@ // SPDX-License-Identifier: Apache-2.0 #include -#include -#include +#include +#include #include -#include -#include -#include +#include +#include #include "compiler_p.h" +#include "internal/icodebuilderfactory.h" +#include "internal/icodebuilder.h" using namespace libscratchcpp; -using namespace vm; -/*! Constructs Compiler. */ +/*! Constructs Compiler using the given context. */ +Compiler::Compiler(CompilerContext *ctx) : + impl(spimpl::make_unique_impl(ctx)) +{ +} + +/*! Constructs Compiler using a new context for the given target. */ Compiler::Compiler(IEngine *engine, Target *target) : impl(spimpl::make_unique_impl(engine, target)) { } -/*! Initializes the Compiler. Useful if you don't use compile(). */ -void Compiler::init() +/*! Returns the Engine of the project. */ +IEngine *Compiler::engine() const { - if (impl->initialized) - return; - - impl->bytecode.clear(); - impl->procedurePrototype = nullptr; - impl->warp = false; - - // Add start instruction - addInstruction(OP_START); - - impl->initialized = true; + return impl->ctx->engine(); } -/*! Compiles the script. Use bytecode() to read the generated bytecode. */ -void Compiler::compile(std::shared_ptr topLevelBlock) +/*! Returns the Target of this compiler. */ +Target *Compiler::target() const { - init(); + return impl->ctx->target(); +} - impl->block = topLevelBlock; +/*! Returns currently compiled block. */ +std::shared_ptr Compiler::block() const +{ + return impl->block; +} - // If this is a top-level block, it might be an edge-activated hat block with a predicate compile function - if (impl->block->topLevel()) { - HatPredicateCompileFunc f = impl->block->hatPredicateCompileFunction(); +/*! Compiles the script starting with the given block. */ +std::shared_ptr Compiler::compile(std::shared_ptr startBlock) +{ + BlockPrototype *procedurePrototype = nullptr; - if (f) { - f(this); + if (startBlock) { + // TODO: Move procedure definition logic to the custom blocks extension + auto input = startBlock->inputAt(0); - // Workaround for register leak warning spam: pause the script after getting the reported value - addFunctionCall([](VirtualMachine *vm) -> unsigned int { - vm->stop(false, false, false); - return 0; - }); + if (input && input->valueBlock()) { + procedurePrototype = input->valueBlock()->mutationPrototype(); - end(); // finished with the predicate - impl->hatPredicateBytecode = impl->bytecode; - init(); // now start the real compilation + if (procedurePrototype && procedurePrototype->procCode().empty()) + procedurePrototype = nullptr; } } - while (impl->block) { - size_t substacks = impl->substackTree.size(); + impl->builder = impl->builderFactory->create(impl->ctx, procedurePrototype); + impl->substackTree.clear(); + impl->substackHit = false; + impl->emptySubstack = false; + impl->warp = false; + impl->block = startBlock; - if (impl->block->compileFunction()) + while (impl->block) { + if (impl->block->compileFunction()) { + assert(impl->customIfStatementCount == 0); impl->block->compile(this); - else { + + if (impl->customIfStatementCount > 0) { + std::cerr << "error: if statement created by block '" << impl->block->opcode() << "' not terminated" << std::endl; + assert(false); + } + + if (impl->emptySubstack) { + impl->emptySubstack = false; + impl->substackEnd(); + } + + if (impl->customLoopCount > 0) { + std::cerr << "error: loop created by block '" << impl->block->opcode() << "' not terminated" << std::endl; + assert(false); + } + } else { std::cout << "warning: unsupported block: " << impl->block->opcode() << std::endl; impl->unsupportedBlocks.insert(impl->block->opcode()); } - if (substacks != impl->substackTree.size()) + if (impl->substackHit) { + impl->substackHit = false; continue; + } if (impl->block) impl->block = impl->block->next(); @@ -81,305 +103,580 @@ void Compiler::compile(std::shared_ptr topLevelBlock) impl->substackEnd(); } - end(); + return impl->builder->finalize(); } -/*! Finalizes the bytecode. Useful if you don't use compile(). */ -void Compiler::end() +/*! + * Optimizes all compiled scripts before they're called for the first time. + * \note Call this only after compiling all scripts. + */ +void Compiler::preoptimize() { - if (!impl->initialized) - return; + impl->ctx->preoptimize(); +} - // Add end instruction (halt) - addInstruction(OP_HALT); +/*! + * Adds a call to the given function.\n + * For example: extern "C" bool some_block(double arg1, const char *arg2) + */ +CompilerValue *Compiler::addFunctionCall(const std::string &functionName, StaticType returnType, const ArgTypes &argTypes, const Args &args) +{ + assert(argTypes.size() == args.size()); + return impl->builder->addFunctionCall(functionName, returnType, argTypes, args); +} - impl->initialized = false; +/*! + * Adds a call to the given function with a target parameter.\n + * For example: extern "C" bool some_block(Target *target, double arg1, const char *arg2) + */ +CompilerValue *Compiler::addTargetFunctionCall(const std::string &functionName, StaticType returnType, const ArgTypes &argTypes, const Args &args) +{ + assert(argTypes.size() == args.size()); + return impl->builder->addTargetFunctionCall(functionName, returnType, argTypes, args); } -/*! Returns the generated bytecode. */ -const std::vector &Compiler::bytecode() const +/*! + * Adds a call to the given function with an execution context parameter.\n + * For example: extern "C" bool some_block(ExecutionContext *ctx, double arg1, const char *arg2) + */ +CompilerValue *Compiler::addFunctionCallWithCtx(const std::string &functionName, StaticType returnType, const ArgTypes &argTypes, const Args &args) { - return impl->bytecode; + assert(argTypes.size() == args.size()); + return impl->builder->addFunctionCallWithCtx(functionName, returnType, argTypes, args); } -/*! Returns the generated hat predicate bytecode (if this is an edge-activated hat). */ -const std::vector &Compiler::hatPredicateBytecode() const +/*! Adds the given constant to the compiled code. */ +CompilerConstant *Compiler::addConstValue(const Value &value) { - return impl->hatPredicateBytecode; + return static_cast(impl->builder->addConstValue(value)); } -/*! Returns the Engine. */ -IEngine *Compiler::engine() const +/*! Adds the index of the current repeat loop to the compiled code. */ +CompilerValue *Compiler::addLoopIndex() { - return impl->engine; + return impl->builder->addLoopIndex(); } -Target *Compiler::target() const +/*! Adds the value of the given local variable to the code. */ +CompilerValue *Compiler::addLocalVariableValue(CompilerLocalVariable *variable) { - return impl->target; + return impl->builder->addLocalVariableValue(variable); } -/*! Returns the list of constant input values. */ -const std::vector &Compiler::constInputValues() const +/*! Adds the value of the given variable to the code. */ +CompilerValue *Compiler::addVariableValue(Variable *variable) { - return impl->constValues; + return impl->builder->addVariableValue(variable); } -/*! Returns the list of constant values. */ -std::vector Compiler::constValues() const +/*! Adds the string representation of the given list to the code. */ +CompilerValue *Compiler::addListContents(List *list) { - std::vector ret; - for (auto value : impl->constValues) { - const auto &menuInfo = impl->constValueMenuInfo.at(value); + return impl->builder->addListContents(list); +} - if (menuInfo.first) - ret.push_back(menuInfo.second); - else - ret.push_back(value->value()); - } - return ret; +/*! Adds the item with index of the given list to the code. */ +CompilerValue *Compiler::addListItem(List *list, CompilerValue *index) +{ + return impl->builder->addListItem(list, index); } -/*! Returns the list of variables. */ -const std::vector &Compiler::variables() const +/*! Adds the index of item of the given list to the code. */ +CompilerValue *Compiler::addListItemIndex(List *list, CompilerValue *item) { - return impl->variables; + return impl->builder->addListItemIndex(list, item); } -/*! Returns the list of pointers to variable values. */ -std::vector Compiler::variablePtrs() const +/*! Adds the result of a list contains item check to the code. */ +CompilerValue *Compiler::addListContains(List *list, CompilerValue *item) { - std::vector ret; - for (auto var : impl->variables) - ret.push_back(var->valuePtr()); - return ret; + return impl->builder->addListContains(list, item); } -/*! Returns the list of lists. */ -const std::vector &Compiler::lists() const +/*! Adds the length of the given list to the code. */ +CompilerValue *Compiler::addListSize(List *list) { - return impl->lists; + return impl->builder->addListSize(list); } -/*! Adds an instruction to the bytecode. */ -void Compiler::addInstruction(Opcode opcode, const std::initializer_list &args) +/*! Adds the procedure argument with the given name to the code. */ +CompilerValue *Compiler::addProcedureArgument(const std::string &name) { - impl->addInstruction(opcode, args); + return impl->builder->addProcedureArgument(name); } -/*! Compiles the given input and adds it to the bytecode. */ -void Compiler::addInput(Input *input) +/*! Compiles the given input (resolved by name) and adds it to the compiled code. */ +CompilerValue *Compiler::addInput(const std::string &name) { - if (!input) { - addInstruction(OP_NULL); - return; - } + return addInput(impl->block->inputAt(impl->block->findInput(name)).get()); +} + +/*! Compiles the given input and adds it to the compiled code. */ +CompilerValue *Compiler::addInput(Input *input) +{ + if (!input) + return addConstValue(Value()); + switch (input->type()) { case Input::Type::Shadow: case Input::Type::NoShadow: { if (input->pointsToDropdownMenu()) - addInstruction(OP_CONST, { impl->constIndex(input->primaryValue(), true, input->selectedMenuItem()) }); + return addConstValue(input->selectedMenuItem()); else { + CompilerValue *ret = nullptr; auto previousBlock = impl->block; impl->block = input->valueBlock(); if (impl->block) { if (impl->block->compileFunction()) - impl->block->compile(this); + ret = impl->block->compile(this); else { std::cout << "warning: unsupported reporter block: " << impl->block->opcode() << std::endl; impl->unsupportedBlocks.insert(impl->block->opcode()); - addInstruction(OP_NULL); + ret = addConstValue(Value()); } } else - addInstruction(OP_CONST, { impl->constIndex(input->primaryValue()) }); + ret = addConstValue(input->primaryValue()->value()); impl->block = previousBlock; + return ret; } - - break; } case Input::Type::ObscuredShadow: { + CompilerValue *ret = nullptr; auto previousBlock = impl->block; impl->block = input->valueBlock(); + if (impl->block) { if (impl->block->compileFunction()) - impl->block->compile(this); + ret = impl->block->compile(this); else { std::cout << "warning: unsupported reporter block: " << impl->block->opcode() << std::endl; impl->unsupportedBlocks.insert(impl->block->opcode()); - addInstruction(OP_NULL); + ret = addConstValue(Value()); } } else - input->primaryValue()->compile(this); + ret = input->primaryValue()->compile(this); + impl->block = previousBlock; - break; + return ret; } } + + return nullptr; } -/*! Compiles the given input (resolved by ID) and adds it to the bytecode. */ -void Compiler::addInput(int id) +/*! Creates an add instruction. */ +CompilerValue *Compiler::createAdd(CompilerValue *operand1, CompilerValue *operand2) { - addInput(input(id)); + return impl->builder->createAdd(operand1, operand2); } -/*! Adds a constant value and an instruction to load it. Useful if you don't have an input for the addInput() method. */ -void libscratchcpp::Compiler::addConstValue(const Value &value) +/*! Creates a subtract instruction. */ +CompilerValue *Compiler::createSub(CompilerValue *operand1, CompilerValue *operand2) { - impl->customConstValues.push_back(std::make_unique()); - impl->customConstValues.back()->setValue(value); - addInstruction(OP_CONST, { impl->constIndex(impl->customConstValues.back().get()) }); + return impl->builder->createSub(operand1, operand2); } -/*! Adds a function call to the bytecode (the OP_EXEC instruction). */ -void Compiler::addFunctionCall(BlockFunc f) +/*! Creates a multiply instruction. */ +CompilerValue *Compiler::createMul(CompilerValue *operand1, CompilerValue *operand2) { - addInstruction(OP_EXEC, { impl->engine->functionIndex(f) }); + return impl->builder->createMul(operand1, operand2); } -/*! Adds an argument to a procedure (custom block). */ -void Compiler::addProcedureArg(const std::string &procCode, const std::string &argName) +/*! Creates a divide instruction. */ +CompilerValue *Compiler::createDiv(CompilerValue *operand1, CompilerValue *operand2) { - if (impl->procedureArgs.find(procCode) != impl->procedureArgs.cend()) { - const auto &procedure = impl->procedureArgs[procCode]; + return impl->builder->createDiv(operand1, operand2); +} - if (std::find(procedure.begin(), procedure.end(), argName) != procedure.end()) - return; - } +/*! Creates a random instruction (Scratch behavior). */ +CompilerValue *Compiler::createRandom(CompilerValue *from, CompilerValue *to) +{ + return impl->builder->createRandom(from, to); +} - impl->procedureArgs[procCode].push_back(argName); +/*! + * Creates a random integer instruction. + * \note Infinity or NaN results in undefined behavior. + */ +CompilerValue *Compiler::createRandomInt(CompilerValue *from, CompilerValue *to) +{ + return impl->builder->createRandomInt(from, to); } -/*! Jumps to the given substack. The second substack is used for example for the if/else block. */ -void Compiler::moveToSubstack(std::shared_ptr substack1, std::shared_ptr substack2, SubstackType type) +/*! Creates an equality comparison instruction. */ +CompilerValue *Compiler::createCmpEQ(CompilerValue *operand1, CompilerValue *operand2) { - impl->substackTree.push_back({ { impl->block, substack2 }, type }); - impl->block = substack1; - if (!impl->block) - impl->substackEnd(); + return impl->builder->createCmpEQ(operand1, operand2); } -/*! Jumps to the given substack. */ -void Compiler::moveToSubstack(std::shared_ptr substack, SubstackType type) +/*! Creates a greater than comparison instruction. */ +CompilerValue *Compiler::createCmpGT(CompilerValue *operand1, CompilerValue *operand2) { - moveToSubstack(substack, nullptr, type); + return impl->builder->createCmpGT(operand1, operand2); } -/*! Makes current script run without screen refresh. */ -void Compiler::warp() +/*! Creates a lower than comparison instruction. */ +CompilerValue *Compiler::createCmpLT(CompilerValue *operand1, CompilerValue *operand2) { - impl->warp = true; - addInstruction(vm::OP_WARP); + return impl->builder->createCmpLT(operand1, operand2); +} + +/*! Creates a string equality comparison (explicitly casts operands to string). */ +CompilerValue *Compiler::createStrCmpEQ(CompilerValue *string1, CompilerValue *string2, bool caseSensitive) +{ + return impl->builder->createStrCmpEQ(string1, string2, caseSensitive); +} + +/*! Creates an AND operation. */ +CompilerValue *Compiler::createAnd(CompilerValue *operand1, CompilerValue *operand2) +{ + return impl->builder->createAnd(operand1, operand2); +} + +/*! Creates an OR operation. */ +CompilerValue *Compiler::createOr(CompilerValue *operand1, CompilerValue *operand2) +{ + return impl->builder->createOr(operand1, operand2); +} + +/*! Creates a NOT operation. */ +CompilerValue *Compiler::createNot(CompilerValue *operand) +{ + return impl->builder->createNot(operand); +} + +/*! Creates a remainder operation. */ +CompilerValue *Compiler::createMod(CompilerValue *num1, CompilerValue *num2) +{ + return impl->builder->createMod(num1, num2); +} + +/*! Creates a round operation. */ +CompilerValue *Compiler::createRound(CompilerValue *num) +{ + return impl->builder->createRound(num); +} + +/*! Creates an abs operation. */ +CompilerValue *Compiler::createAbs(CompilerValue *num) +{ + return impl->builder->createAbs(num); +} + +/*! Creates a floor operation. */ +CompilerValue *Compiler::createFloor(CompilerValue *num) +{ + return impl->builder->createFloor(num); +} + +/*! Creates a ceiling operation. */ +CompilerValue *Compiler::createCeil(CompilerValue *num) +{ + return impl->builder->createCeil(num); +} + +/*! Creates a square root operation. */ +CompilerValue *Compiler::createSqrt(CompilerValue *num) +{ + return impl->builder->createSqrt(num); +} + +/*! Creates a sin operation. */ +CompilerValue *Compiler::createSin(CompilerValue *num) +{ + return impl->builder->createSin(num); +} + +/*! Creates a cos operation. */ +CompilerValue *Compiler::createCos(CompilerValue *num) +{ + return impl->builder->createCos(num); } -/*! Returns the input with the given ID. */ -Input *Compiler::input(int id) const +/*! Creates a tan operation. */ +CompilerValue *Compiler::createTan(CompilerValue *num) { - return impl->block->findInputById(id); + return impl->builder->createTan(num); } -/*! Returns the field with the given ID. */ -Field *Compiler::field(int id) const +/*! Creates an asin operation. */ +CompilerValue *Compiler::createAsin(CompilerValue *num) { - return impl->block->findFieldById(id); + return impl->builder->createAsin(num); } -/*! Returns the block in the given input. Same as input(id)->valueBlock(), but with a null pointer check. */ -std::shared_ptr Compiler::inputBlock(int id) const +/*! Creates an acos operation. */ +CompilerValue *Compiler::createAcos(CompilerValue *num) { - auto in = input(id); - return in ? in->valueBlock() : nullptr; + return impl->builder->createAcos(num); } -/*! Returns the index of the given variable. */ -unsigned int Compiler::variableIndex(std::shared_ptr varEntity) +/*! Creates an atan operation. */ +CompilerValue *Compiler::createAtan(CompilerValue *num) { - assert(varEntity); - auto var = dynamic_cast(varEntity.get()); - assert(var); - auto it = std::find(impl->variables.begin(), impl->variables.end(), var); - if (it != impl->variables.end()) - return it - impl->variables.begin(); - impl->variables.push_back(var); - return impl->variables.size() - 1; + return impl->builder->createAtan(num); } -/*! Returns the index of the given list. */ -unsigned int Compiler::listIndex(std::shared_ptr listEntity) +/*! Creates an ln operation. */ +CompilerValue *Compiler::createLn(CompilerValue *num) { - auto list = dynamic_cast(listEntity.get()); - auto it = std::find(impl->lists.begin(), impl->lists.end(), list); - if (it != impl->lists.end()) - return it - impl->lists.begin(); - impl->lists.push_back(list); - return impl->lists.size() - 1; + return impl->builder->createLn(num); } -/*! Returns the index of the given constant input value. */ -unsigned int Compiler::constIndex(InputValue *value) +/*! Creates a log10 operation. */ +CompilerValue *Compiler::createLog10(CompilerValue *num) { - return impl->constIndex(value); + return impl->builder->createLog10(num); } -/*! Returns the index of the procedure code of the given block. */ -unsigned int Compiler::procedureIndex(const std::string &proc) +/*! Creates an e^x operation. */ +CompilerValue *Compiler::createExp(CompilerValue *num) { - auto it = std::find(impl->procedures.begin(), impl->procedures.end(), proc); - if (it != impl->procedures.end()) - return it - impl->procedures.begin(); - impl->procedures.push_back(proc); - return impl->procedures.size() - 1; + return impl->builder->createExp(num); } -/*! Returns the index of the argument of the given procedure (custom block). */ -long Compiler::procedureArgIndex(const std::string &procCode, const std::string &argName) +/*! Creates a 10^x operation. */ +CompilerValue *Compiler::createExp10(CompilerValue *num) { - if (impl->procedureArgs.count(procCode) == 0) { - std::cout << "warning: could not find custom block '" << procCode << "'" << std::endl; - return -1; + return impl->builder->createExp10(num); +} + +/*! Creates a select instruction (ternary operator). */ +CompilerValue *Compiler::createSelect(CompilerValue *cond, CompilerValue *trueValue, CompilerValue *falseValue, StaticType valueType) +{ + return impl->builder->createSelect(cond, trueValue, falseValue, valueType); +} + +/*! Creates a local variable with the given type. */ +CompilerLocalVariable *Compiler::createLocalVariable(StaticType type) +{ + return impl->builder->createLocalVariable(type); +} + +/*! Creates a local variable write operation. */ +void Compiler::createLocalVariableWrite(CompilerLocalVariable *variable, CompilerValue *value) +{ + impl->builder->createLocalVariableWrite(variable, value); +} + +/*! Creates a variable write operation. */ +void Compiler::createVariableWrite(Variable *variable, CompilerValue *value) +{ + impl->builder->createVariableWrite(variable, value); +} + +/*! Creates a clear list operation. */ +void Compiler::createListClear(List *list) +{ + impl->builder->createListClear(list); +} + +/*! + * Creates a remove item from list operation. + * \note The index starts with 0 and is cast to number, special strings like "last" are not handled. + */ +void Compiler::createListRemove(List *list, CompilerValue *index) +{ + impl->builder->createListRemove(list, index); +} + +/*! Creates a list append operation. */ +void Compiler::createListAppend(List *list, CompilerValue *item) +{ + impl->builder->createListAppend(list, item); +} + +/*! Creates a list insert operation. */ +void Compiler::createListInsert(List *list, CompilerValue *index, CompilerValue *item) +{ + impl->builder->createListInsert(list, index, item); +} + +/*! Creates a list replace operation. */ +void Compiler::createListReplace(List *list, CompilerValue *index, CompilerValue *item) +{ + impl->builder->createListReplace(list, index, item); +} + +/*! + * Starts a custom if statement. + * \note The if statement must be terminated using endIf() after compiling your block. + */ +void Compiler::beginIfStatement(CompilerValue *cond) +{ + impl->builder->beginIfStatement(cond); + impl->customIfStatementCount++; +} + +/*! Starts the else branch of custom if statement. */ +void Compiler::beginElseBranch() +{ + impl->builder->beginElseBranch(); +} + +/*! Ends custom if statement. */ +void Compiler::endIf() +{ + if (impl->customIfStatementCount == 0) { + std::cerr << "error: called Compiler::endIf() without an if statement"; + assert(false); + return; } - const std::vector args = impl->procedureArgs[procCode]; - auto it = std::find(args.begin(), args.end(), argName); - if (it != args.end()) - return it - args.begin(); - std::cout << "warning: could not find argument '" << argName << "' in custom block '" << procCode << "'" << std::endl; - return -1; + + impl->builder->endIf(); + impl->customIfStatementCount--; } -/*! Returns the prototype of the current custom block. */ -BlockPrototype *Compiler::procedurePrototype() const +/*! + * Begins a custom while loop. + * \note The loop must be terminated with endLoop() after compiling your block. + */ +void Compiler::beginWhileLoop(CompilerValue *cond) { - return impl->procedurePrototype; + impl->builder->beginWhileLoop(cond); + impl->customLoopCount++; } -/*! Sets the prototype of the current custom block. */ -void Compiler::setProcedurePrototype(BlockPrototype *prototype) +/*! + * Begins a custom repeat until loop. + * \note The loop must be terminated with endLoop() after compiling your block. + */ +void Compiler::beginRepeatUntilLoop(CompilerValue *cond) { - impl->procedurePrototype = prototype; + impl->builder->beginRepeatUntilLoop(cond); + impl->customLoopCount++; } -/*! Returns unsupported block opcodes which were found when compiling. */ -const std::unordered_set &libscratchcpp::Compiler::unsupportedBlocks() const +/*! Begins a while/until loop condition. */ +void Compiler::beginLoopCondition() { - return impl->unsupportedBlocks; + impl->builder->beginLoopCondition(); } -/*! Returns the list of custom block prototypes. */ -const std::vector &Compiler::procedures() const +/*! Ends custom loop. */ +void Compiler::endLoop() { - return impl->procedures; + if (impl->customLoopCount == 0) { + std::cerr << "error: called Compiler::endLoop() without a loop"; + assert(false); + return; + } + + impl->builder->endLoop(); + impl->customLoopCount--; } -/*! Returns the current block. */ -const std::shared_ptr &Compiler::block() const +/*! Jumps to the given if substack. */ +void Compiler::moveToIf(CompilerValue *cond, std::shared_ptr substack) { - return impl->block; + if (!substack) + return; // ignore empty if statements + + impl->substackHit = true; + impl->substackTree.push_back({ { impl->block, nullptr }, CompilerPrivate::SubstackType::IfStatement }); + impl->block = substack; + impl->builder->beginIfStatement(cond); +} + +/*! Jumps to the given if/else substack. The second substack is used for the else branch. */ +void Compiler::moveToIfElse(CompilerValue *cond, std::shared_ptr substack1, std::shared_ptr substack2) +{ + if (!substack1 && !substack2) + return; // ignore empty if statements + + impl->substackHit = true; + impl->substackTree.push_back({ { impl->block, substack2 }, CompilerPrivate::SubstackType::IfStatement }); + impl->block = substack1; + impl->builder->beginIfStatement(cond); + + if (!impl->block) + impl->emptySubstack = true; +} + +/*! Jumps to the given repeat loop substack. */ +void Compiler::moveToRepeatLoop(CompilerValue *count, std::shared_ptr substack) +{ + impl->substackHit = true; + impl->substackTree.push_back({ { impl->block, nullptr }, CompilerPrivate::SubstackType::Loop }); + impl->block = substack; + impl->builder->beginRepeatLoop(count); + + if (!impl->block) + impl->emptySubstack = true; +} + +/*! Jumps to the given while loop substack. */ +void Compiler::moveToWhileLoop(CompilerValue *cond, std::shared_ptr substack) +{ + impl->substackHit = true; + impl->substackTree.push_back({ { impl->block, nullptr }, CompilerPrivate::SubstackType::Loop }); + impl->block = substack; + impl->builder->beginWhileLoop(cond); + + if (!impl->block) + impl->emptySubstack = true; +} + +/*! Jumps to the given until loop substack. */ +void Compiler::moveToRepeatUntilLoop(CompilerValue *cond, std::shared_ptr substack) +{ + impl->substackHit = true; + impl->substackTree.push_back({ { impl->block, nullptr }, CompilerPrivate::SubstackType::Loop }); + impl->block = substack; + impl->builder->beginRepeatUntilLoop(cond); + + if (!impl->block) + impl->emptySubstack = true; +} + +/*! Makes current script run without screen refresh. */ +void Compiler::warp() +{ + impl->warp = true; +} + +/*! Creates a suspend instruction. */ +void Compiler::createYield() +{ + impl->builder->yield(); +} + +/*! Creates a stop script instruction. */ +void Compiler::createStop() +{ + impl->builder->createStop(); +} + +/*! Creates a call to the procedure with the given prototype. */ +void Compiler::createProcedureCall(BlockPrototype *prototype, const libscratchcpp::Compiler::Args &args) +{ + impl->builder->createProcedureCall(prototype, args); +} + +/*! Convenience method which returns the field with the given name. */ +Input *Compiler::input(const std::string &name) const +{ + return impl->block->inputAt(impl->block->findInput(name)).get(); +} + +/*! Convenience method which returns the field with the given name. */ +Field *Compiler::field(const std::string &name) const +{ + return impl->block->fieldAt(impl->block->findField(name)).get(); +} + +/*! Returns unsupported block opcodes which were found when compiling. */ +const std::unordered_set &Compiler::unsupportedBlocks() const +{ + return impl->unsupportedBlocks; } -/*! Sets the current block. Useful if you don't use compile(). */ -void Compiler::setBlock(std::shared_ptr block) +/*! Creates a compiler context for the given target. */ +std::shared_ptr Compiler::createContext(IEngine *engine, Target *target) { - impl->block = block; + CompilerPrivate::initBuilderFactory(); + return CompilerPrivate::builderFactory->createCtx(engine, target); } diff --git a/src/engine/compiler_p.cpp b/src/engine/compiler_p.cpp index 7e3e5f1c..55de0af4 100644 --- a/src/engine/compiler_p.cpp +++ b/src/engine/compiler_p.cpp @@ -1,55 +1,57 @@ // SPDX-License-Identifier: Apache-2.0 +#include #include #include "compiler_p.h" +#include "internal/codebuilderfactory.h" +#include "internal/icodebuilder.h" + using namespace libscratchcpp; -using namespace vm; -CompilerPrivate::CompilerPrivate(IEngine *engine, Target *target) : - engine(engine), - target(target) +CompilerPrivate::CompilerPrivate(CompilerContext *ctx) : + ctx(ctx) { - assert(engine); + assert(ctx); + initBuilderFactory(); } -void CompilerPrivate::addInstruction(vm::Opcode opcode, std::initializer_list args) +CompilerPrivate::CompilerPrivate(IEngine *engine, Target *target) : + ctxPtr(Compiler::createContext(engine, target)), + ctx(ctxPtr.get()) { - bytecode.push_back(opcode); - for (auto arg : args) - bytecode.push_back(arg); + initBuilderFactory(); } -unsigned int CompilerPrivate::constIndex(InputValue *value, bool pointsToDropdownMenu, const std::string &selectedMenuItem) +void CompilerPrivate::initBuilderFactory() { - auto it = std::find(constValues.begin(), constValues.end(), value); - if (it != constValues.end()) - return it - constValues.begin(); - constValues.push_back(value); - constValueMenuInfo[value] = { pointsToDropdownMenu, selectedMenuItem }; - return constValues.size() - 1; + if (!builderFactory) + builderFactory = CodeBuilderFactory::instance().get(); } void CompilerPrivate::substackEnd() { - auto parent = substackTree.back(); + auto &parent = substackTree.back(); + switch (parent.second) { - case Compiler::SubstackType::Loop: - // Break the frame at the end of the loop so that other scripts can run within the frame - // This won't happen in "warp" scripts - if (!warp) - addInstruction(OP_BREAK_FRAME); - addInstruction(OP_LOOP_END); + case SubstackType::Loop: + // Yield at loop end if not running without screen refresh + /*if (!warp) + builder->yield();*/ + + builder->endLoop(); break; - case Compiler::SubstackType::IfStatement: + + case SubstackType::IfStatement: if (parent.first.second) { - addInstruction(OP_ELSE); + builder->beginElseBranch(); block = parent.first.second; - substackTree[substackTree.size() - 1].first.second = nullptr; + parent.first.second = nullptr; return; } else - addInstruction(OP_ENDIF); + builder->endIf(); + break; } @@ -61,6 +63,7 @@ void CompilerPrivate::substackEnd() block = nullptr; substackTree.pop_back(); + if (!block && !substackTree.empty()) substackEnd(); } diff --git a/src/engine/compiler_p.h b/src/engine/compiler_p.h index 5a396a7a..593accbf 100644 --- a/src/engine/compiler_p.h +++ b/src/engine/compiler_p.h @@ -2,44 +2,50 @@ #pragma once -#include +#include +#include #include -#include -#include namespace libscratchcpp { +class CompilerContext; +class IEngine; +class Target; +class Block; +class CompilerValue; +class ICodeBuilderFactory; +class ICodeBuilder; + struct CompilerPrivate { - CompilerPrivate(IEngine *engine, Target *target); - CompilerPrivate(const CompilerPrivate &) = delete; + enum class SubstackType + { + Loop, + IfStatement + }; - void addInstruction(vm::Opcode opcode, std::initializer_list args = {}); + CompilerPrivate(CompilerContext *ctx); + CompilerPrivate(IEngine *engine, Target *target); - unsigned int constIndex(InputValue *value, bool pointsToDropdownMenu = false, const std::string &selectedMenuItem = ""); + static void initBuilderFactory(); void substackEnd(); - IEngine *engine = nullptr; - Target *target = nullptr; + std::shared_ptr ctxPtr; // for self-managed contexts + CompilerContext *ctx = nullptr; + std::shared_ptr block; - std::vector, std::shared_ptr>, Compiler::SubstackType>> substackTree; - - bool initialized = false; - - std::vector bytecode; - std::vector hatPredicateBytecode; - std::vector constValues; - std::vector> customConstValues; - std::unordered_map> constValueMenuInfo; // input value, - std::vector variables; - std::vector lists; - std::vector procedures; - std::unordered_map> procedureArgs; - BlockPrototype *procedurePrototype = nullptr; + int customIfStatementCount = 0; + int customLoopCount = 0; + std::vector, std::shared_ptr>, SubstackType>> substackTree; + bool substackHit = false; + bool emptySubstack = false; bool warp = false; + static inline ICodeBuilderFactory *builderFactory = nullptr; + std::shared_ptr builder; + std::unordered_set unsupportedBlocks; }; diff --git a/src/dev/engine/compilerconstant.cpp b/src/engine/compilerconstant.cpp similarity index 90% rename from src/dev/engine/compilerconstant.cpp rename to src/engine/compilerconstant.cpp index fb7dcf3b..3dd2f15a 100644 --- a/src/dev/engine/compilerconstant.cpp +++ b/src/engine/compilerconstant.cpp @@ -1,6 +1,6 @@ // SPDX-License-Identifier: Apache-2.0 -#include +#include #include "compilerconstant_p.h" diff --git a/src/dev/engine/compilerconstant_p.cpp b/src/engine/compilerconstant_p.cpp similarity index 100% rename from src/dev/engine/compilerconstant_p.cpp rename to src/engine/compilerconstant_p.cpp diff --git a/src/dev/engine/compilerconstant_p.h b/src/engine/compilerconstant_p.h similarity index 100% rename from src/dev/engine/compilerconstant_p.h rename to src/engine/compilerconstant_p.h diff --git a/src/dev/engine/compilercontext.cpp b/src/engine/compilercontext.cpp similarity index 92% rename from src/dev/engine/compilercontext.cpp rename to src/engine/compilercontext.cpp index 12bc613e..fa78ad57 100644 --- a/src/dev/engine/compilercontext.cpp +++ b/src/engine/compilercontext.cpp @@ -1,6 +1,6 @@ // SPDX-License-Identifier: Apache-2.0 -#include +#include #include "compilercontext_p.h" diff --git a/src/dev/engine/compilercontext_p.cpp b/src/engine/compilercontext_p.cpp similarity index 100% rename from src/dev/engine/compilercontext_p.cpp rename to src/engine/compilercontext_p.cpp diff --git a/src/dev/engine/compilercontext_p.h b/src/engine/compilercontext_p.h similarity index 100% rename from src/dev/engine/compilercontext_p.h rename to src/engine/compilercontext_p.h diff --git a/src/dev/engine/compilerlocalvariable.cpp b/src/engine/compilerlocalvariable.cpp similarity index 82% rename from src/dev/engine/compilerlocalvariable.cpp rename to src/engine/compilerlocalvariable.cpp index 8a8329a4..98d0341b 100644 --- a/src/dev/engine/compilerlocalvariable.cpp +++ b/src/engine/compilerlocalvariable.cpp @@ -1,7 +1,7 @@ // SPDX-License-Identifier: Apache-2.0 -#include -#include +#include +#include #include "compilerlocalvariable_p.h" diff --git a/src/dev/engine/compilerlocalvariable_p.cpp b/src/engine/compilerlocalvariable_p.cpp similarity index 100% rename from src/dev/engine/compilerlocalvariable_p.cpp rename to src/engine/compilerlocalvariable_p.cpp diff --git a/src/dev/engine/compilerlocalvariable_p.h b/src/engine/compilerlocalvariable_p.h similarity index 100% rename from src/dev/engine/compilerlocalvariable_p.h rename to src/engine/compilerlocalvariable_p.h diff --git a/src/dev/engine/compilervalue.cpp b/src/engine/compilervalue.cpp similarity index 92% rename from src/dev/engine/compilervalue.cpp rename to src/engine/compilervalue.cpp index 9a601321..fa8dad25 100644 --- a/src/dev/engine/compilervalue.cpp +++ b/src/engine/compilervalue.cpp @@ -1,6 +1,6 @@ // SPDX-License-Identifier: Apache-2.0 -#include +#include #include "compilervalue_p.h" diff --git a/src/dev/engine/compilervalue_p.cpp b/src/engine/compilervalue_p.cpp similarity index 100% rename from src/dev/engine/compilervalue_p.cpp rename to src/engine/compilervalue_p.cpp diff --git a/src/dev/engine/compilervalue_p.h b/src/engine/compilervalue_p.h similarity index 87% rename from src/dev/engine/compilervalue_p.h rename to src/engine/compilervalue_p.h index 99a9112e..a49df5a3 100644 --- a/src/dev/engine/compilervalue_p.h +++ b/src/engine/compilervalue_p.h @@ -2,7 +2,7 @@ #pragma once -#include +#include namespace libscratchcpp { diff --git a/src/dev/engine/executioncontext.cpp b/src/engine/executioncontext.cpp similarity index 96% rename from src/dev/engine/executioncontext.cpp rename to src/engine/executioncontext.cpp index fb0d9327..06109d55 100644 --- a/src/dev/engine/executioncontext.cpp +++ b/src/engine/executioncontext.cpp @@ -1,6 +1,6 @@ // SPDX-License-Identifier: Apache-2.0 -#include +#include #include #include "executioncontext_p.h" diff --git a/src/dev/engine/executioncontext_p.cpp b/src/engine/executioncontext_p.cpp similarity index 86% rename from src/dev/engine/executioncontext_p.cpp rename to src/engine/executioncontext_p.cpp index 94dd1072..49fe5369 100644 --- a/src/dev/engine/executioncontext_p.cpp +++ b/src/engine/executioncontext_p.cpp @@ -1,7 +1,7 @@ // SPDX-License-Identifier: Apache-2.0 #include "executioncontext_p.h" -#include "../../engine/internal/randomgenerator.h" +#include "internal/randomgenerator.h" using namespace libscratchcpp; diff --git a/src/dev/engine/executioncontext_p.h b/src/engine/executioncontext_p.h similarity index 91% rename from src/dev/engine/executioncontext_p.h rename to src/engine/executioncontext_p.h index 367452aa..8e5ec0d4 100644 --- a/src/dev/engine/executioncontext_p.h +++ b/src/engine/executioncontext_p.h @@ -4,7 +4,7 @@ #include -#include "../../engine/internal/stacktimer.h" +#include "internal/stacktimer.h" namespace libscratchcpp { diff --git a/src/dev/engine/internal/codebuilderfactory.cpp b/src/engine/internal/codebuilderfactory.cpp similarity index 100% rename from src/dev/engine/internal/codebuilderfactory.cpp rename to src/engine/internal/codebuilderfactory.cpp diff --git a/src/dev/engine/internal/codebuilderfactory.h b/src/engine/internal/codebuilderfactory.h similarity index 100% rename from src/dev/engine/internal/codebuilderfactory.h rename to src/engine/internal/codebuilderfactory.h diff --git a/src/engine/internal/engine.cpp b/src/engine/internal/engine.cpp index c0bd3173..24a1a36c 100644 --- a/src/engine/internal/engine.cpp +++ b/src/engine/internal/engine.cpp @@ -7,12 +7,8 @@ #include #include #include -#ifdef USE_LLVM -#include -#include -#else #include -#endif +#include #include #include #include @@ -33,11 +29,7 @@ #include "timer.h" #include "clock.h" #include "audio/iaudioengine.h" -#ifdef USE_LLVM -#include "dev/blocks/blocks.h" -#else #include "blocks/blocks.h" -#endif #include "scratch/monitor_p.h" using namespace libscratchcpp; @@ -87,7 +79,6 @@ void Engine::clear() m_threads.clear(); m_threadsToStop.clear(); m_scripts.clear(); - m_functions.clear(); m_whenTouchingObjectHats.clear(); m_greenFlagHats.clear(); @@ -271,35 +262,18 @@ void Engine::compile() // Compile scripts to bytecode for (auto target : m_targets) { std::cout << "Compiling scripts in target " << target->name() << "..." << std::endl; -#ifdef USE_LLVM auto ctx = Compiler::createContext(this, target.get()); m_compilerContexts[target.get()] = ctx; Compiler compiler(ctx.get()); -#else - std::unordered_map procedureBytecodeMap; - Compiler compiler(this, target.get()); -#endif const auto &blocks = target->blocks(); + for (auto block : blocks) { if (block->topLevel() && !block->isTopLevelReporter() && !block->shadow()) { auto ext = blockExtension(block->opcode()); if (ext) { auto script = std::make_shared