Skip to content

Commit f98e1d1

Browse files
committed
Implement control_foreach
1 parent 9e8440d commit f98e1d1

File tree

4 files changed

+130
-0
lines changed

4 files changed

+130
-0
lines changed

src/dev/blocks/controlblocks.cpp

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
#include <scratchcpp/dev/executioncontext.h>
1010
#include <scratchcpp/thread.h>
1111
#include <scratchcpp/istacktimer.h>
12+
#include <scratchcpp/variable.h>
1213

1314
#include "controlblocks.h"
1415

@@ -35,6 +36,7 @@ void ControlBlocks::registerBlocks(IEngine *engine)
3536
engine->addCompileFunction(this, "control_wait_until", &compileWaitUntil);
3637
engine->addCompileFunction(this, "control_repeat_until", &compileRepeatUntil);
3738
engine->addCompileFunction(this, "control_while", &compileWhile);
39+
engine->addCompileFunction(this, "control_for_each", &compileForEach);
3840
}
3941

4042
CompilerValue *ControlBlocks::compileForever(Compiler *compiler)
@@ -123,6 +125,17 @@ CompilerValue *ControlBlocks::compileWhile(Compiler *compiler)
123125
return nullptr;
124126
}
125127

128+
CompilerValue *ControlBlocks::compileForEach(Compiler *compiler)
129+
{
130+
Variable *var = static_cast<Variable *>(compiler->field("VARIABLE")->valuePtr().get());
131+
assert(var);
132+
auto substack = compiler->input("SUBSTACK");
133+
compiler->moveToRepeatLoop(compiler->addInput("VALUE"), substack ? substack->valueBlock() : nullptr);
134+
auto index = compiler->createAdd(compiler->addLoopIndex(), compiler->addConstValue(1));
135+
compiler->createVariableWrite(var, index);
136+
return nullptr;
137+
}
138+
126139
extern "C" void control_stop_all(ExecutionContext *ctx)
127140
{
128141
ctx->engine()->stop();

src/dev/blocks/controlblocks.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ class ControlBlocks : public IExtension
2525
static CompilerValue *compileWaitUntil(Compiler *compiler);
2626
static CompilerValue *compileRepeatUntil(Compiler *compiler);
2727
static CompilerValue *compileWhile(Compiler *compiler);
28+
static CompilerValue *compileForEach(Compiler *compiler);
2829
};
2930

3031
} // namespace libscratchcpp

test/dev/blocks/control_blocks_test.cpp

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,12 @@
44
#include <scratchcpp/sprite.h>
55
#include <scratchcpp/block.h>
66
#include <scratchcpp/input.h>
7+
#include <scratchcpp/field.h>
78
#include <scratchcpp/script.h>
89
#include <scratchcpp/thread.h>
910
#include <scratchcpp/dev/executablecode.h>
1011
#include <scratchcpp/dev/executioncontext.h>
12+
#include <scratchcpp/variable.h>
1113

1214
#include <enginemock.h>
1315
#include <stacktimermock.h>
@@ -646,3 +648,109 @@ TEST_F(ControlBlocksTest, While)
646648
}
647649
}
648650
}
651+
652+
TEST_F(ControlBlocksTest, ForEach)
653+
{
654+
auto target = std::make_shared<Sprite>();
655+
auto var1 = std::make_shared<Variable>("", "");
656+
auto var2 = std::make_shared<Variable>("", "");
657+
target->addVariable(var1);
658+
target->addVariable(var2);
659+
660+
{
661+
ScriptBuilder builder(m_extension.get(), m_engine, target);
662+
663+
builder.addBlock("control_for_each");
664+
665+
auto substack = std::make_shared<Block>("", "test_print");
666+
auto input = std::make_shared<Input>("STRING", Input::Type::ObscuredShadow);
667+
input->primaryValue()->setValuePtr(var1);
668+
substack->addInput(input);
669+
670+
builder.addObscuredInput("SUBSTACK", substack);
671+
672+
builder.addValueInput("VALUE", 5);
673+
builder.addEntityField("VARIABLE", var1);
674+
675+
builder.addBlock("control_for_each");
676+
substack = std::make_shared<Block>("", "test_print_test");
677+
builder.addObscuredInput("SUBSTACK", substack);
678+
builder.addNullObscuredInput("VALUE");
679+
builder.addEntityField("VARIABLE", var2);
680+
681+
builder.build();
682+
683+
var1->setValue(10);
684+
testing::internal::CaptureStdout();
685+
builder.run();
686+
ASSERT_EQ(testing::internal::GetCapturedStdout(), "1\n2\n3\n4\n5\n");
687+
ASSERT_EQ(var1->value(), 5);
688+
}
689+
690+
m_engine->clear();
691+
target = std::make_shared<Sprite>();
692+
target->addVariable(var1);
693+
694+
{
695+
ScriptBuilder builder(m_extension.get(), m_engine, target);
696+
697+
builder.addBlock("control_for_each");
698+
699+
auto substack = std::make_shared<Block>("", "test_print");
700+
auto input = std::make_shared<Input>("STRING", Input::Type::ObscuredShadow);
701+
input->primaryValue()->setValuePtr(var1);
702+
substack->addInput(input);
703+
704+
auto setVar = std::make_shared<Block>("", "test_set_var");
705+
substack->setNext(setVar);
706+
setVar->setParent(substack);
707+
auto field = std::make_shared<Field>("VARIABLE", "");
708+
setVar->addField(field);
709+
input = std::make_shared<Input>("VALUE", Input::Type::Shadow);
710+
input->setPrimaryValue(0);
711+
setVar->addInput(input);
712+
713+
auto printAgain = std::make_shared<Block>("", "test_print");
714+
setVar->setNext(printAgain);
715+
printAgain->setParent(setVar);
716+
input = std::make_shared<Input>("STRING", Input::Type::ObscuredShadow);
717+
printAgain->addInput(input);
718+
719+
builder.addObscuredInput("SUBSTACK", substack);
720+
721+
builder.addValueInput("VALUE", 3);
722+
builder.addEntityField("VARIABLE", var1);
723+
724+
field->setValuePtr(var1);
725+
input->primaryValue()->setValuePtr(var1);
726+
727+
builder.build();
728+
729+
var1->setValue(7);
730+
testing::internal::CaptureStdout();
731+
builder.run();
732+
ASSERT_EQ(testing::internal::GetCapturedStdout(), "1\n0\n2\n0\n3\n0\n");
733+
ASSERT_EQ(var1->value(), 0);
734+
}
735+
736+
m_engine->clear();
737+
target = std::make_shared<Sprite>();
738+
target->addVariable(var1);
739+
740+
{
741+
ScriptBuilder builder(m_extension.get(), m_engine, target);
742+
builder.addBlock("control_for_each");
743+
builder.addValueInput("VALUE", "Infinity");
744+
builder.addEntityField("VARIABLE", var1);
745+
746+
builder.build();
747+
m_engine->start();
748+
749+
for (int i = 0; i < 2; i++) {
750+
m_engine->step();
751+
ASSERT_TRUE(m_engine->isRunning());
752+
}
753+
754+
ASSERT_GT(var1->value(), 0);
755+
}
756+
}

test/dev/blocks/util.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
#include <scratchcpp/dev/compiler.h>
33
#include <scratchcpp/dev/compilerconstant.h>
44
#include <scratchcpp/value.h>
5+
#include <scratchcpp/field.h>
6+
#include <scratchcpp/variable.h>
57
#include <iostream>
68

79
#include "util.h"
@@ -30,6 +32,12 @@ void registerBlocks(IEngine *engine, IExtension *extension)
3032
});
3133

3234
engine->addCompileFunction(extension, "test_condition", [](Compiler *compiler) -> CompilerValue * { return compiler->addFunctionCall("test_condition", Compiler::StaticType::Bool); });
35+
36+
engine->addCompileFunction(extension, "test_set_var", [](Compiler *compiler) -> CompilerValue * {
37+
Variable *var = static_cast<Variable *>(compiler->field("VARIABLE")->valuePtr().get());
38+
compiler->createVariableWrite(var, compiler->addInput("VALUE"));
39+
return nullptr;
40+
});
3341
}
3442

3543
extern "C" void test_print(const char *str)

0 commit comments

Comments
 (0)