Skip to content

Commit 30fe5dc

Browse files
authored
Merge pull request #609 from scratchcpp/llvm_control_blocks
LLVM: Implement control blocks
2 parents bf5029f + 2388d9f commit 30fe5dc

33 files changed

+1892
-24
lines changed

CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ target_sources(scratchcpp
5959
include/scratchcpp/sprite.h
6060
include/scratchcpp/textbubble.h
6161
include/scratchcpp/itimer.h
62+
include/scratchcpp/istacktimer.h
6263
include/scratchcpp/keyevent.h
6364
include/scratchcpp/rect.h
6465
include/scratchcpp/igraphicseffect.h

include/scratchcpp/dev/compiler.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ class LIBSCRATCHCPP_EXPORT Compiler
5151
CompilerValue *addTargetFunctionCall(const std::string &functionName, StaticType returnType = StaticType::Void, const ArgTypes &argTypes = {}, const Args &args = {});
5252
CompilerValue *addFunctionCallWithCtx(const std::string &functionName, StaticType returnType = StaticType::Void, const ArgTypes &argTypes = {}, const Args &args = {});
5353
CompilerConstant *addConstValue(const Value &value);
54+
CompilerValue *addLoopIndex();
5455
CompilerValue *addVariableValue(Variable *variable);
5556
CompilerValue *addListContents(List *list);
5657
CompilerValue *addListItem(List *list, CompilerValue *index);
@@ -115,6 +116,9 @@ class LIBSCRATCHCPP_EXPORT Compiler
115116
void moveToRepeatUntilLoop(CompilerValue *cond, std::shared_ptr<Block> substack);
116117
void warp();
117118

119+
void createYield();
120+
void createStop();
121+
118122
Input *input(const std::string &name) const;
119123
Field *field(const std::string &name) const;
120124

include/scratchcpp/dev/executioncontext.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ namespace libscratchcpp
1111
class Thread;
1212
class IEngine;
1313
class Promise;
14+
class IStackTimer;
1415
class ExecutionContextPrivate;
1516

1617
/*! \brief The ExecutionContext represents the execution context of a target (can be a clone) with variables, lists, etc. */
@@ -27,6 +28,9 @@ class LIBSCRATCHCPP_EXPORT ExecutionContext
2728
std::shared_ptr<Promise> promise() const;
2829
void setPromise(std::shared_ptr<Promise> promise);
2930

31+
IStackTimer *stackTimer() const;
32+
void setStackTimer(IStackTimer *newStackTimer);
33+
3034
private:
3135
spimpl::unique_impl_ptr<ExecutionContextPrivate> impl;
3236
};

include/scratchcpp/istacktimer.h

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
// SPDX-License-Identifier: Apache-2.0
2+
3+
#pragma once
4+
5+
#include "global.h"
6+
7+
namespace libscratchcpp
8+
{
9+
10+
/*!
11+
* \brief The IStackTimer interface represents a timer that can be used by blocks.
12+
*
13+
* You can get a stack timer using ExecutionContext#stackTimer().
14+
*/
15+
class LIBSCRATCHCPP_EXPORT IStackTimer
16+
{
17+
public:
18+
virtual ~IStackTimer() { }
19+
20+
/*! Starts the timer. */
21+
virtual void start(double seconds) = 0;
22+
23+
/*! Stops the timer. */
24+
virtual void stop() = 0;
25+
26+
/*! Returns true if the timer has been stopped using stop() or wasn't used at all. */
27+
virtual bool stopped() const = 0;
28+
29+
/*! Returns true if the timer has elapsed. */
30+
virtual bool elapsed() const = 0;
31+
};
32+
33+
} // namespace libscratchcpp

src/dev/blocks/controlblocks.cpp

Lines changed: 214 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,17 @@
11
// SPDX-License-Identifier: Apache-2.0
22

3+
#include <scratchcpp/iengine.h>
4+
#include <scratchcpp/dev/compiler.h>
5+
#include <scratchcpp/dev/compilerconstant.h>
6+
#include <scratchcpp/value.h>
7+
#include <scratchcpp/input.h>
8+
#include <scratchcpp/field.h>
9+
#include <scratchcpp/dev/executioncontext.h>
10+
#include <scratchcpp/thread.h>
11+
#include <scratchcpp/istacktimer.h>
12+
#include <scratchcpp/variable.h>
13+
#include <scratchcpp/sprite.h>
14+
315
#include "controlblocks.h"
416

517
using namespace libscratchcpp;
@@ -16,4 +28,206 @@ std::string ControlBlocks::description() const
1628

1729
void ControlBlocks::registerBlocks(IEngine *engine)
1830
{
31+
engine->addCompileFunction(this, "control_forever", &compileForever);
32+
engine->addCompileFunction(this, "control_repeat", &compileRepeat);
33+
engine->addCompileFunction(this, "control_if", &compileIf);
34+
engine->addCompileFunction(this, "control_if_else", &compileIfElse);
35+
engine->addCompileFunction(this, "control_stop", &compileStop);
36+
engine->addCompileFunction(this, "control_wait", &compileWait);
37+
engine->addCompileFunction(this, "control_wait_until", &compileWaitUntil);
38+
engine->addCompileFunction(this, "control_repeat_until", &compileRepeatUntil);
39+
engine->addCompileFunction(this, "control_while", &compileWhile);
40+
engine->addCompileFunction(this, "control_for_each", &compileForEach);
41+
engine->addCompileFunction(this, "control_start_as_clone", &compileStartAsClone);
42+
engine->addCompileFunction(this, "control_create_clone_of", &compileCreateCloneOf);
43+
engine->addCompileFunction(this, "control_delete_this_clone", &compileDeleteThisClone);
44+
}
45+
46+
CompilerValue *ControlBlocks::compileForever(Compiler *compiler)
47+
{
48+
auto substack = compiler->input("SUBSTACK");
49+
compiler->beginLoopCondition();
50+
compiler->moveToWhileLoop(compiler->addConstValue(true), substack ? substack->valueBlock() : nullptr);
51+
return nullptr;
52+
}
53+
54+
CompilerValue *ControlBlocks::compileRepeat(Compiler *compiler)
55+
{
56+
auto substack = compiler->input("SUBSTACK");
57+
compiler->moveToRepeatLoop(compiler->addInput("TIMES"), substack ? substack->valueBlock() : nullptr);
58+
return nullptr;
59+
}
60+
61+
CompilerValue *ControlBlocks::compileIf(Compiler *compiler)
62+
{
63+
auto substack = compiler->input("SUBSTACK");
64+
compiler->moveToIf(compiler->addInput("CONDITION"), substack ? substack->valueBlock() : nullptr);
65+
return nullptr;
66+
}
67+
68+
CompilerValue *ControlBlocks::compileIfElse(Compiler *compiler)
69+
{
70+
auto substack = compiler->input("SUBSTACK");
71+
auto substack2 = compiler->input("SUBSTACK2");
72+
compiler->moveToIfElse(compiler->addInput("CONDITION"), substack ? substack->valueBlock() : nullptr, substack2 ? substack2->valueBlock() : nullptr);
73+
return nullptr;
74+
}
75+
76+
CompilerValue *ControlBlocks::compileStop(Compiler *compiler)
77+
{
78+
Field *option = compiler->field("STOP_OPTION");
79+
80+
if (option) {
81+
std::string str = option->value().toString();
82+
83+
if (str == "all")
84+
compiler->addFunctionCallWithCtx("control_stop_all", Compiler::StaticType::Void);
85+
else if (str == "this script")
86+
compiler->createStop();
87+
else if (str == "other scripts in sprite" || str == "other scripts in stage")
88+
compiler->addFunctionCallWithCtx("control_stop_other_scripts_in_target", Compiler::StaticType::Void);
89+
}
90+
91+
return nullptr;
92+
}
93+
94+
CompilerValue *ControlBlocks::compileWait(Compiler *compiler)
95+
{
96+
auto duration = compiler->addInput("DURATION");
97+
compiler->addFunctionCallWithCtx("control_start_wait", Compiler::StaticType::Void, { Compiler::StaticType::Number }, { duration });
98+
compiler->createYield();
99+
100+
compiler->beginLoopCondition();
101+
auto elapsed = compiler->addFunctionCallWithCtx("control_stack_timer_elapsed", Compiler::StaticType::Bool);
102+
compiler->beginRepeatUntilLoop(elapsed);
103+
compiler->endLoop();
104+
105+
return nullptr;
106+
}
107+
108+
CompilerValue *ControlBlocks::compileWaitUntil(Compiler *compiler)
109+
{
110+
compiler->beginLoopCondition();
111+
compiler->beginRepeatUntilLoop(compiler->addInput("CONDITION"));
112+
compiler->endLoop();
113+
return nullptr;
114+
}
115+
116+
CompilerValue *ControlBlocks::compileRepeatUntil(Compiler *compiler)
117+
{
118+
auto substack = compiler->input("SUBSTACK");
119+
compiler->beginLoopCondition();
120+
compiler->moveToRepeatUntilLoop(compiler->addInput("CONDITION"), substack ? substack->valueBlock() : nullptr);
121+
return nullptr;
122+
}
123+
124+
CompilerValue *ControlBlocks::compileWhile(Compiler *compiler)
125+
{
126+
auto substack = compiler->input("SUBSTACK");
127+
compiler->beginLoopCondition();
128+
compiler->moveToWhileLoop(compiler->addInput("CONDITION"), substack ? substack->valueBlock() : nullptr);
129+
return nullptr;
130+
}
131+
132+
CompilerValue *ControlBlocks::compileForEach(Compiler *compiler)
133+
{
134+
Variable *var = static_cast<Variable *>(compiler->field("VARIABLE")->valuePtr().get());
135+
assert(var);
136+
auto substack = compiler->input("SUBSTACK");
137+
compiler->moveToRepeatLoop(compiler->addInput("VALUE"), substack ? substack->valueBlock() : nullptr);
138+
auto index = compiler->createAdd(compiler->addLoopIndex(), compiler->addConstValue(1));
139+
compiler->createVariableWrite(var, index);
140+
return nullptr;
141+
}
142+
143+
CompilerValue *ControlBlocks::compileStartAsClone(Compiler *compiler)
144+
{
145+
compiler->engine()->addCloneInitScript(compiler->block());
146+
return nullptr;
147+
}
148+
149+
CompilerValue *ControlBlocks::compileCreateCloneOf(Compiler *compiler)
150+
{
151+
Input *input = compiler->input("CLONE_OPTION");
152+
153+
if (input->pointsToDropdownMenu()) {
154+
std::string spriteName = input->selectedMenuItem();
155+
156+
if (spriteName == "_myself_")
157+
compiler->addTargetFunctionCall("control_create_clone_of_myself");
158+
else {
159+
auto index = compiler->engine()->findTarget(spriteName);
160+
CompilerValue *arg = compiler->addConstValue(index);
161+
compiler->addFunctionCallWithCtx("control_create_clone_by_index", Compiler::StaticType::Void, { Compiler::StaticType::Number }, { arg });
162+
}
163+
} else {
164+
CompilerValue *arg = compiler->addInput("CLONE_OPTION");
165+
compiler->addFunctionCallWithCtx("control_create_clone", Compiler::StaticType::Void, { Compiler::StaticType::String }, { arg });
166+
}
167+
168+
return nullptr;
169+
}
170+
171+
CompilerValue *ControlBlocks::compileDeleteThisClone(Compiler *compiler)
172+
{
173+
compiler->addTargetFunctionCall("control_delete_this_clone");
174+
return nullptr;
175+
}
176+
177+
extern "C" void control_stop_all(ExecutionContext *ctx)
178+
{
179+
ctx->engine()->stop();
180+
}
181+
182+
extern "C" void control_stop_other_scripts_in_target(ExecutionContext *ctx)
183+
{
184+
Thread *thread = ctx->thread();
185+
ctx->engine()->stopTarget(thread->target(), thread);
186+
}
187+
188+
extern "C" void control_start_wait(ExecutionContext *ctx, double seconds)
189+
{
190+
ctx->stackTimer()->start(seconds);
191+
ctx->engine()->requestRedraw();
192+
}
193+
194+
extern "C" bool control_stack_timer_elapsed(ExecutionContext *ctx)
195+
{
196+
return ctx->stackTimer()->elapsed();
197+
}
198+
199+
extern "C" void control_create_clone_of_myself(Target *target)
200+
{
201+
if (!target->isStage())
202+
static_cast<Sprite *>(target)->clone();
203+
}
204+
205+
extern "C" void control_create_clone_by_index(ExecutionContext *ctx, double index)
206+
{
207+
Target *target = ctx->engine()->targetAt(index);
208+
209+
if (!target->isStage())
210+
static_cast<Sprite *>(target)->clone();
211+
}
212+
213+
extern "C" void control_create_clone(ExecutionContext *ctx, const char *spriteName)
214+
{
215+
if (strcmp(spriteName, "_myself_") == 0)
216+
control_create_clone_of_myself(ctx->thread()->target());
217+
else {
218+
IEngine *engine = ctx->engine();
219+
auto index = engine->findTarget(spriteName);
220+
Target *target = engine->targetAt(index);
221+
222+
if (!target->isStage())
223+
static_cast<Sprite *>(target)->clone();
224+
}
225+
}
226+
227+
extern "C" void control_delete_this_clone(Target *target)
228+
{
229+
if (!target->isStage()) {
230+
target->engine()->stopTarget(target, nullptr);
231+
static_cast<Sprite *>(target)->deleteClone();
232+
}
19233
}

src/dev/blocks/controlblocks.h

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,21 @@ class ControlBlocks : public IExtension
1414
std::string description() const override;
1515

1616
void registerBlocks(IEngine *engine) override;
17+
18+
private:
19+
static CompilerValue *compileForever(Compiler *compiler);
20+
static CompilerValue *compileRepeat(Compiler *compiler);
21+
static CompilerValue *compileIf(Compiler *compiler);
22+
static CompilerValue *compileIfElse(Compiler *compiler);
23+
static CompilerValue *compileStop(Compiler *compiler);
24+
static CompilerValue *compileWait(Compiler *compiler);
25+
static CompilerValue *compileWaitUntil(Compiler *compiler);
26+
static CompilerValue *compileRepeatUntil(Compiler *compiler);
27+
static CompilerValue *compileWhile(Compiler *compiler);
28+
static CompilerValue *compileForEach(Compiler *compiler);
29+
static CompilerValue *compileStartAsClone(Compiler *compiler);
30+
static CompilerValue *compileCreateCloneOf(Compiler *compiler);
31+
static CompilerValue *compileDeleteThisClone(Compiler *compiler);
1732
};
1833

1934
} // namespace libscratchcpp

0 commit comments

Comments
 (0)