Skip to content

Commit 00bba37

Browse files
committed
Compiler: Implement if statements
1 parent f26e8cf commit 00bba37

File tree

5 files changed

+206
-6
lines changed

5 files changed

+206
-6
lines changed

include/scratchcpp/dev/compiler.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ class LIBSCRATCHCPP_EXPORT Compiler
4343
void moveToLoop(std::shared_ptr<Block> substack);
4444
void warp();
4545

46+
Input *input(const std::string &name) const;
4647
Field *field(const std::string &name) const;
4748

4849
const std::unordered_set<std::string> &unsupportedBlocks() const;

src/dev/engine/compiler.cpp

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,8 @@ std::shared_ptr<libscratchcpp::Block> Compiler::block() const
3939
std::shared_ptr<ExecutableCode> Compiler::compile(std::shared_ptr<Block> startBlock)
4040
{
4141
impl->builder = impl->builderFactory->create(startBlock->id());
42+
impl->substackTree.clear();
43+
impl->substackHit = false;
4244
impl->warp = false;
4345
impl->block = startBlock;
4446

@@ -52,10 +54,13 @@ std::shared_ptr<ExecutableCode> Compiler::compile(std::shared_ptr<Block> startBl
5254
impl->unsupportedBlocks.insert(impl->block->opcode());
5355
}
5456

55-
if (substacks != impl->substackTree.size())
57+
if (impl->substackHit) {
58+
impl->substackHit = false;
5659
continue;
60+
}
5761

58-
impl->block = impl->block->next();
62+
if (impl->block)
63+
impl->block = impl->block->next();
5964

6065
if (!impl->block && !impl->substackTree.empty())
6166
impl->substackEnd();
@@ -100,18 +105,25 @@ void Compiler::addInput(const std::string &name)
100105
/*! Jumps to the given if substack. */
101106
void Compiler::moveToIf(std::shared_ptr<Block> substack)
102107
{
108+
if (!substack)
109+
return; // ignore empty if statements
110+
111+
impl->substackHit = true;
103112
impl->substackTree.push_back({ { impl->block, nullptr }, CompilerPrivate::SubstackType::IfStatement });
104113
impl->block = substack;
105-
106-
if (!impl->block)
107-
impl->substackEnd();
114+
impl->builder->beginIfStatement();
108115
}
109116

110117
/*! Jumps to the given if/else substack. The second substack is used for the else branch. */
111118
void Compiler::moveToIfElse(std::shared_ptr<Block> substack1, std::shared_ptr<Block> substack2)
112119
{
120+
if (!substack1 && !substack2)
121+
return; // ignore empty if statements
122+
123+
impl->substackHit = true;
113124
impl->substackTree.push_back({ { impl->block, substack2 }, CompilerPrivate::SubstackType::IfStatement });
114125
impl->block = substack1;
126+
impl->builder->beginIfStatement();
115127

116128
if (!impl->block)
117129
impl->substackEnd();
@@ -120,6 +132,7 @@ void Compiler::moveToIfElse(std::shared_ptr<Block> substack1, std::shared_ptr<Bl
120132
/*! Jumps to the given loop substack. */
121133
void Compiler::moveToLoop(std::shared_ptr<Block> substack)
122134
{
135+
impl->substackHit = true;
123136
impl->substackTree.push_back({ { impl->block, nullptr }, CompilerPrivate::SubstackType::Loop });
124137
impl->block = substack;
125138

src/dev/engine/compiler_p.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ CompilerPrivate::CompilerPrivate(IEngine *engine, Target *target) :
1919

2020
void CompilerPrivate::substackEnd()
2121
{
22-
auto parent = substackTree.back();
22+
auto &parent = substackTree.back();
2323

2424
switch (parent.second) {
2525
case SubstackType::Loop:

src/dev/engine/compiler_p.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ struct CompilerPrivate
3232

3333
std::shared_ptr<Block> block;
3434
std::vector<std::pair<std::pair<std::shared_ptr<Block>, std::shared_ptr<Block>>, SubstackType>> substackTree;
35+
bool substackHit = false;
3536
bool warp = false;
3637

3738
static inline ICodeBuilderFactory *builderFactory = nullptr;

test/dev/compiler/compiler_test.cpp

Lines changed: 185 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -216,6 +216,191 @@ TEST_F(CompilerTest, AddInput)
216216
compile(compiler, block);
217217
}
218218

219+
TEST_F(CompilerTest, MoveToIf)
220+
{
221+
Compiler compiler(&m_engine, &m_target);
222+
EXPECT_CALL(*m_builder, beginElseBranch).Times(0);
223+
224+
auto if1 = std::make_shared<Block>("", "if");
225+
if1->setCompileFunction([](Compiler *compiler) {
226+
EXPECT_CALL(*m_builder, beginIfStatement).Times(0);
227+
EXPECT_CALL(*m_builder, endIf).Times(0);
228+
compiler->moveToIf(nullptr);
229+
});
230+
231+
auto if2 = std::make_shared<Block>("", "if");
232+
if1->setNext(if2);
233+
if2->setParent(if1);
234+
if2->setCompileFunction([](Compiler *compiler) {
235+
EXPECT_CALL(*m_builder, beginIfStatement());
236+
EXPECT_CALL(*m_builder, addConstValue(Value()));
237+
EXPECT_CALL(*m_builder, endIf());
238+
compiler->moveToIf(compiler->input("SUBSTACK")->valueBlock());
239+
});
240+
241+
auto substack1 = std::make_shared<Block>("", "substack");
242+
substack1->setParent(if2);
243+
substack1->setCompileFunction([](Compiler *compiler) { compiler->addConstValue(Value()); });
244+
245+
auto input = std::make_shared<Input>("SUBSTACK", Input::Type::NoShadow);
246+
input->setValueBlock(substack1);
247+
if2->addInput(input);
248+
249+
compile(compiler, if1);
250+
}
251+
252+
TEST_F(CompilerTest, MoveToIfElse)
253+
{
254+
Compiler compiler(&m_engine, &m_target);
255+
256+
auto if1 = std::make_shared<Block>("", "if");
257+
if1->setCompileFunction([](Compiler *compiler) {
258+
EXPECT_CALL(*m_builder, beginIfStatement).Times(0);
259+
EXPECT_CALL(*m_builder, beginElseBranch).Times(0);
260+
EXPECT_CALL(*m_builder, endIf).Times(0);
261+
compiler->moveToIfElse(nullptr, nullptr);
262+
});
263+
264+
auto if2 = std::make_shared<Block>("", "if");
265+
if1->setNext(if2);
266+
if2->setParent(if1);
267+
if2->setCompileFunction([](Compiler *compiler) {
268+
EXPECT_CALL(*m_builder, beginIfStatement());
269+
EXPECT_CALL(*m_builder, addConstValue(Value(1)));
270+
EXPECT_CALL(*m_builder, beginElseBranch());
271+
EXPECT_CALL(*m_builder, addConstValue(Value(2)));
272+
EXPECT_CALL(*m_builder, endIf());
273+
compiler->moveToIfElse(compiler->input("SUBSTACK")->valueBlock(), compiler->input("SUBSTACK2")->valueBlock());
274+
});
275+
276+
auto substack1 = std::make_shared<Block>("", "substack");
277+
substack1->setParent(if2);
278+
substack1->setCompileFunction([](Compiler *compiler) { compiler->addConstValue(1); });
279+
280+
auto substack2 = std::make_shared<Block>("", "substack");
281+
substack2->setParent(if2);
282+
substack2->setCompileFunction([](Compiler *compiler) { compiler->addConstValue(2); });
283+
284+
auto input = std::make_shared<Input>("SUBSTACK", Input::Type::NoShadow);
285+
input->setValueBlock(substack1);
286+
if2->addInput(input);
287+
input = std::make_shared<Input>("SUBSTACK2", Input::Type::NoShadow);
288+
input->setValueBlock(substack2);
289+
if2->addInput(input);
290+
291+
// Nested
292+
auto if3 = std::make_shared<Block>("", "if");
293+
if2->setNext(if3);
294+
if3->setParent(if2);
295+
if3->setCompileFunction([](Compiler *compiler) {
296+
EXPECT_CALL(*m_builder, beginIfStatement()).Times(3);
297+
EXPECT_CALL(*m_builder, beginElseBranch()).Times(3);
298+
EXPECT_CALL(*m_builder, endIf()).Times(3);
299+
EXPECT_CALL(*m_builder, addConstValue(Value(1)));
300+
EXPECT_CALL(*m_builder, addConstValue(Value(2)));
301+
EXPECT_CALL(*m_builder, addConstValue(Value(3)));
302+
EXPECT_CALL(*m_builder, addConstValue(Value(4)));
303+
compiler->moveToIfElse(compiler->input("SUBSTACK")->valueBlock(), compiler->input("SUBSTACK2")->valueBlock());
304+
});
305+
306+
// if
307+
auto ifSubstack1 = std::make_shared<Block>("", "if");
308+
ifSubstack1->setParent(if3);
309+
ifSubstack1->setCompileFunction([](Compiler *compiler) { compiler->moveToIfElse(compiler->input("SUBSTACK")->valueBlock(), compiler->input("SUBSTACK2")->valueBlock()); });
310+
311+
substack1 = std::make_shared<Block>("", "substack");
312+
substack1->setParent(ifSubstack1);
313+
substack1->setCompileFunction([](Compiler *compiler) { compiler->addConstValue(1); });
314+
315+
substack2 = std::make_shared<Block>("", "substack");
316+
substack2->setParent(ifSubstack1);
317+
substack2->setCompileFunction([](Compiler *compiler) { compiler->addConstValue(2); });
318+
319+
input = std::make_shared<Input>("SUBSTACK", Input::Type::NoShadow);
320+
input->setValueBlock(substack1);
321+
ifSubstack1->addInput(input);
322+
input = std::make_shared<Input>("SUBSTACK2", Input::Type::NoShadow);
323+
input->setValueBlock(substack2);
324+
ifSubstack1->addInput(input);
325+
326+
// else
327+
auto ifSubstack2 = std::make_shared<Block>("", "if");
328+
ifSubstack2->setParent(if3);
329+
ifSubstack2->setCompileFunction([](Compiler *compiler) { compiler->moveToIfElse(compiler->input("SUBSTACK")->valueBlock(), compiler->input("SUBSTACK2")->valueBlock()); });
330+
331+
substack1 = std::make_shared<Block>("", "substack");
332+
substack1->setParent(ifSubstack2);
333+
substack1->setCompileFunction([](Compiler *compiler) { compiler->addConstValue(3); });
334+
335+
substack2 = std::make_shared<Block>("", "substack");
336+
substack2->setParent(ifSubstack2);
337+
substack2->setCompileFunction([](Compiler *compiler) { compiler->addConstValue(4); });
338+
339+
input = std::make_shared<Input>("SUBSTACK", Input::Type::NoShadow);
340+
input->setValueBlock(substack1);
341+
ifSubstack2->addInput(input);
342+
input = std::make_shared<Input>("SUBSTACK2", Input::Type::NoShadow);
343+
input->setValueBlock(substack2);
344+
ifSubstack2->addInput(input);
345+
346+
// End if
347+
input = std::make_shared<Input>("SUBSTACK", Input::Type::NoShadow);
348+
input->setValueBlock(ifSubstack1);
349+
if3->addInput(input);
350+
input = std::make_shared<Input>("SUBSTACK2", Input::Type::NoShadow);
351+
input->setValueBlock(ifSubstack2);
352+
if3->addInput(input);
353+
354+
// Empty 'then' branch
355+
auto if4 = std::make_shared<Block>("", "if");
356+
if3->setNext(if4);
357+
if4->setParent(if3);
358+
if4->setCompileFunction([](Compiler *compiler) {
359+
EXPECT_CALL(*m_builder, beginIfStatement());
360+
EXPECT_CALL(*m_builder, beginElseBranch());
361+
EXPECT_CALL(*m_builder, addConstValue(Value(2)));
362+
EXPECT_CALL(*m_builder, endIf());
363+
compiler->moveToIfElse(nullptr, compiler->input("SUBSTACK2")->valueBlock());
364+
});
365+
366+
substack2 = std::make_shared<Block>("", "substack");
367+
substack2->setParent(if4);
368+
substack2->setCompileFunction([](Compiler *compiler) { compiler->addConstValue(2); });
369+
370+
input = std::make_shared<Input>("SUBSTACK2", Input::Type::NoShadow);
371+
input->setValueBlock(substack2);
372+
if4->addInput(input);
373+
374+
// Empty 'else' branch
375+
auto if5 = std::make_shared<Block>("", "if");
376+
if4->setNext(if5);
377+
if5->setParent(if4);
378+
if5->setCompileFunction([](Compiler *compiler) {
379+
EXPECT_CALL(*m_builder, beginIfStatement());
380+
EXPECT_CALL(*m_builder, addConstValue(Value(1)));
381+
EXPECT_CALL(*m_builder, beginElseBranch()).Times(0);
382+
EXPECT_CALL(*m_builder, endIf());
383+
compiler->moveToIfElse(compiler->input("SUBSTACK")->valueBlock(), nullptr);
384+
});
385+
386+
substack1 = std::make_shared<Block>("", "substack");
387+
substack1->setParent(if5);
388+
substack1->setCompileFunction([](Compiler *compiler) { compiler->addConstValue(1); });
389+
390+
input = std::make_shared<Input>("SUBSTACK", Input::Type::NoShadow);
391+
input->setValueBlock(substack1);
392+
if5->addInput(input);
393+
394+
// Code after the if statement
395+
auto block = std::make_shared<Block>("", "");
396+
block->setParent(if5);
397+
if5->setNext(block);
398+
block->setCompileFunction([](Compiler *compiler) { compiler->addConstValue("after"); });
399+
400+
EXPECT_CALL(*m_builder, addConstValue(Value("after")));
401+
compile(compiler, if1);
402+
}
403+
219404
TEST_F(CompilerTest, Field)
220405
{
221406
Compiler compiler(&m_engine, &m_target);

0 commit comments

Comments
 (0)