Skip to content

Commit 454c182

Browse files
committed
Add new Compiler class
1 parent bc135d0 commit 454c182

File tree

23 files changed

+815
-70
lines changed

23 files changed

+815
-70
lines changed

CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ if (LIBSCRATCHCPP_USE_LLVM)
7070
target_compile_definitions(scratchcpp PUBLIC USE_LLVM)
7171
target_sources(scratchcpp
7272
PUBLIC
73+
include/scratchcpp/dev/compiler.h
7374
include/scratchcpp/dev/executablecode.h
7475
include/scratchcpp/dev/executioncontext.h
7576
)

include/scratchcpp/block.h

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,7 @@ class LIBSCRATCHCPP_EXPORT Block : public Entity
2525
Block(const std::string &id, const std::string &opcode);
2626
Block(const Block &) = delete;
2727

28-
#ifndef USE_LLVM
2928
void compile(Compiler *compiler);
30-
#endif
3129

3230
const std::string &opcode() const;
3331

@@ -75,13 +73,11 @@ class LIBSCRATCHCPP_EXPORT Block : public Entity
7573
void setTarget(Target *newTarget);
7674
Target *target() const;
7775

78-
#ifndef USE_LLVM
7976
BlockComp compileFunction() const;
8077
void setCompileFunction(BlockComp newCompileFunction);
8178

8279
BlockComp hatPredicateCompileFunction() const;
8380
void setHatPredicateCompileFunction(HatPredicateCompileFunc newHatPredicateCompileFunction);
84-
#endif // USE_LLVM
8581

8682
bool mutationHasNext() const;
8783
void setMutationHasNext(bool newMutationHasNext);

include/scratchcpp/dev/compiler.h

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
// SPDX-License-Identifier: Apache-2.0
2+
3+
#pragma once
4+
5+
#include <unordered_set>
6+
7+
#include "../global.h"
8+
#include "../spimpl.h"
9+
10+
namespace libscratchcpp
11+
{
12+
13+
class IEngine;
14+
class Target;
15+
class ExecutableCode;
16+
class Variable;
17+
class List;
18+
class Input;
19+
class Field;
20+
class CompilerPrivate;
21+
22+
/*! \brief The Compiler class provides API for compiling Scratch scripts. */
23+
class LIBSCRATCHCPP_EXPORT Compiler
24+
{
25+
public:
26+
Compiler(IEngine *engine, Target *target);
27+
Compiler(const Compiler &) = delete;
28+
29+
IEngine *engine() const;
30+
Target *target() const;
31+
std::shared_ptr<Block> block() const;
32+
33+
std::shared_ptr<ExecutableCode> compile(std::shared_ptr<Block> startBlock);
34+
35+
void addFunctionCall(const std::string &functionName, int argCount, bool returns);
36+
void addConstValue(const Value &value);
37+
void addVariableValue(Variable *variable);
38+
void addListContents(List *list);
39+
void addInput(const std::string &name);
40+
41+
void moveToIf(std::shared_ptr<Block> substack);
42+
void moveToIfElse(std::shared_ptr<Block> substack1, std::shared_ptr<Block> substack2);
43+
void moveToLoop(std::shared_ptr<Block> substack);
44+
void warp();
45+
46+
Field *field(const std::string &name) const;
47+
48+
const std::unordered_set<std::string> &unsupportedBlocks() const;
49+
50+
private:
51+
void addInput(Input *input);
52+
53+
spimpl::unique_impl_ptr<CompilerPrivate> impl;
54+
};
55+
56+
} // namespace libscratchcpp

include/scratchcpp/global.h

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,7 @@ namespace libscratchcpp
2424
{
2525

2626
class VirtualMachine;
27-
#ifndef USE_LLVM
2827
class Compiler;
29-
#endif
3028
class Block;
3129
class Value;
3230

@@ -37,14 +35,12 @@ class Value;
3735
*/
3836
using BlockFunc = unsigned int (*)(VirtualMachine *vm);
3937

40-
#ifndef USE_LLVM
4138
/*!
4239
* \typedef BlockComp
4340
*
4441
* BlockComp is a function pointer for functions which are used to compile blocks to bytecode.
4542
*/
4643
using BlockComp = void (*)(Compiler *);
47-
#endif // USE_LLVM
4844

4945
/*!
5046
* \typedef MonitorNameFunc
@@ -60,14 +56,12 @@ using MonitorNameFunc = const std::string &(*)(Block *);
6056
*/
6157
using MonitorChangeFunc = void (*)(Block *, const Value &newValue);
6258

63-
#ifndef USE_LLVM
6459
/*!
6560
* \typedef HatPredicateCompileFunc
6661
*
6762
* HatPredicateCompileFunc is a function pointer for functions which are used to compile edge-activated hat predicates to bytecode.
6863
*/
6964
using HatPredicateCompileFunc = void (*)(Compiler *vm);
70-
#endif // USE_LLVM
7165

7266
} // namespace libscratchcpp
7367

include/scratchcpp/iengine.h

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -225,7 +225,6 @@ class LIBSCRATCHCPP_EXPORT IEngine
225225
/*! Returns the list of block functions. */
226226
virtual const std::vector<BlockFunc> &blockFunctions() const = 0;
227227

228-
#ifndef USE_LLVM
229228
/*!
230229
* Call this from IExtension#registerBlocks() to add a compile function to a block section.
231230
* \see <a href="extensions.html">Extensions</a>
@@ -238,7 +237,6 @@ class LIBSCRATCHCPP_EXPORT IEngine
238237
* \see <a href="extensions.html">Extensions</a>
239238
*/
240239
virtual void addHatPredicateCompileFunction(IExtension *extension, const std::string &opcode, HatPredicateCompileFunc f) = 0;
241-
#endif // USE_LLVM
242240

243241
/*!
244242
* Call this from IExtension#registerBlocks() to add a monitor name function to a block section.
@@ -358,13 +356,11 @@ class LIBSCRATCHCPP_EXPORT IEngine
358356
/*! Sets the list of monitors. */
359357
virtual void setMonitors(const std::vector<std::shared_ptr<Monitor>> &newMonitors) = 0;
360358

361-
#ifndef USE_LLVM
362359
/*! Creates a monitor for the given variable if it's missing and returns it. */
363360
virtual Monitor *createVariableMonitor(std::shared_ptr<Variable> var, const std::string &opcode, const std::string &varFieldName, int varFieldId, BlockComp compileFunction) = 0;
364361

365362
/*! Creates a monitor for the given list if it's missing and returns it. */
366363
virtual Monitor *createListMonitor(std::shared_ptr<List> list, const std::string &opcode, const std::string &listFieldName, int listFieldId, BlockComp compileFunction) = 0;
367-
#endif // USE_LLVM
368364

369365
/*! Emits when a monitor is added. */
370366
virtual sigslot::signal<Monitor *> &monitorAdded() = 0;

include/scratchcpp/inputvalue.h

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,6 @@ namespace libscratchcpp
1111
{
1212

1313
class Block;
14-
#ifndef USE_LLVM
15-
class Compiler;
16-
#endif
1714
class Entity;
1815
class InputValuePrivate;
1916

@@ -37,9 +34,7 @@ class LIBSCRATCHCPP_EXPORT InputValue
3734

3835
InputValue(Type type = Type::Number);
3936

40-
#ifndef USE_LLVM
4137
void compile(Compiler *compiler);
42-
#endif
4338

4439
Type type() const;
4540
void setType(Type newType);

src/dev/engine/CMakeLists.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
target_sources(scratchcpp
22
PRIVATE
3+
compiler.cpp
4+
compiler_p.cpp
5+
compiler_p.h
36
executioncontext.cpp
47
executioncontext_p.cpp
58
executioncontext_p.h

src/dev/engine/compiler.cpp

Lines changed: 199 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,199 @@
1+
// SPDX-License-Identifier: Apache-2.0
2+
3+
#include <scratchcpp/dev/compiler.h>
4+
#include <scratchcpp/block.h>
5+
#include <scratchcpp/input.h>
6+
#include <scratchcpp/inputvalue.h>
7+
8+
#include "compiler_p.h"
9+
#include "internal/icodebuilderfactory.h"
10+
#include "internal/icodebuilder.h"
11+
12+
using namespace libscratchcpp;
13+
14+
/*! Constructs Compiler. */
15+
Compiler::Compiler(IEngine *engine, Target *target) :
16+
impl(spimpl::make_unique_impl<CompilerPrivate>(engine, target))
17+
{
18+
}
19+
20+
/*! Returns the Engine of the project. */
21+
IEngine *Compiler::engine() const
22+
{
23+
return impl->engine;
24+
}
25+
26+
/*! Returns the Target of this compiler. */
27+
Target *Compiler::target() const
28+
{
29+
return impl->target;
30+
}
31+
32+
/*! Returns currently compiled block. */
33+
std::shared_ptr<libscratchcpp::Block> Compiler::block() const
34+
{
35+
return impl->block;
36+
}
37+
38+
/*! Compiles the script starting with the given block. */
39+
std::shared_ptr<ExecutableCode> Compiler::compile(std::shared_ptr<Block> startBlock)
40+
{
41+
impl->builder = impl->builderFactory->create(startBlock->id());
42+
impl->warp = false;
43+
impl->block = startBlock;
44+
45+
while (impl->block) {
46+
size_t substacks = impl->substackTree.size();
47+
48+
if (impl->block->compileFunction())
49+
impl->block->compile(this);
50+
else {
51+
std::cout << "warning: unsupported block: " << impl->block->opcode() << std::endl;
52+
impl->unsupportedBlocks.insert(impl->block->opcode());
53+
}
54+
55+
if (substacks != impl->substackTree.size())
56+
continue;
57+
58+
impl->block = impl->block->next();
59+
60+
if (!impl->block && !impl->substackTree.empty())
61+
impl->substackEnd();
62+
}
63+
64+
return impl->builder->finalize();
65+
}
66+
67+
/*!
68+
* Adds a call to the given function.\n
69+
* For example: extern "C" some_block(ValueData *ret, ValueData *arg1, ValueData *arg2) has 2 arguments
70+
*/
71+
void Compiler::addFunctionCall(const std::string &functionName, int argCount, bool returns)
72+
{
73+
impl->builder->addFunctionCall(functionName, argCount, returns);
74+
}
75+
76+
/*! Adds a constant value to the compiled code. */
77+
void Compiler::addConstValue(const Value &value)
78+
{
79+
impl->builder->addConstValue(value);
80+
}
81+
82+
/*! Adds the given variable to the code (to read it). */
83+
void Compiler::addVariableValue(Variable *variable)
84+
{
85+
impl->builder->addVariableValue(variable);
86+
}
87+
88+
/*! Adds the given list to the code (to read its string representation). */
89+
void Compiler::addListContents(List *list)
90+
{
91+
impl->builder->addListContents(list);
92+
}
93+
94+
/*! Compiles the given input (resolved by name) and adds it to the compiled code. */
95+
void Compiler::addInput(const std::string &name)
96+
{
97+
addInput(impl->block->inputAt(impl->block->findInput(name)).get());
98+
}
99+
100+
/*! Jumps to the given if substack. */
101+
void Compiler::moveToIf(std::shared_ptr<Block> substack)
102+
{
103+
impl->substackTree.push_back({ { impl->block, nullptr }, CompilerPrivate::SubstackType::IfStatement });
104+
impl->block = substack;
105+
106+
if (!impl->block)
107+
impl->substackEnd();
108+
}
109+
110+
/*! Jumps to the given if/else substack. The second substack is used for the else branch. */
111+
void Compiler::moveToIfElse(std::shared_ptr<Block> substack1, std::shared_ptr<Block> substack2)
112+
{
113+
impl->substackTree.push_back({ { impl->block, substack2 }, CompilerPrivate::SubstackType::IfStatement });
114+
impl->block = substack1;
115+
116+
if (!impl->block)
117+
impl->substackEnd();
118+
}
119+
120+
/*! Jumps to the given loop substack. */
121+
void Compiler::moveToLoop(std::shared_ptr<Block> substack)
122+
{
123+
impl->substackTree.push_back({ { impl->block, nullptr }, CompilerPrivate::SubstackType::Loop });
124+
impl->block = substack;
125+
126+
if (!impl->block)
127+
impl->substackEnd();
128+
}
129+
130+
/*! Makes current script run without screen refresh. */
131+
void Compiler::warp()
132+
{
133+
impl->warp = true;
134+
}
135+
136+
/*! Convenience method which returns the field with the given name. */
137+
Field *Compiler::field(const std::string &name) const
138+
{
139+
return impl->block->fieldAt(impl->block->findField(name)).get();
140+
}
141+
142+
/*! Returns unsupported block opcodes which were found when compiling. */
143+
const std::unordered_set<std::string> &Compiler::unsupportedBlocks() const
144+
{
145+
return impl->unsupportedBlocks;
146+
}
147+
148+
void Compiler::addInput(Input *input)
149+
{
150+
if (!input) {
151+
addConstValue(Value());
152+
return;
153+
}
154+
155+
switch (input->type()) {
156+
case Input::Type::Shadow:
157+
case Input::Type::NoShadow: {
158+
if (input->pointsToDropdownMenu())
159+
addConstValue(input->selectedMenuItem());
160+
else {
161+
auto previousBlock = impl->block;
162+
impl->block = input->valueBlock();
163+
164+
if (impl->block) {
165+
if (impl->block->compileFunction())
166+
impl->block->compile(this);
167+
else {
168+
std::cout << "warning: unsupported reporter block: " << impl->block->opcode() << std::endl;
169+
impl->unsupportedBlocks.insert(impl->block->opcode());
170+
addConstValue(Value());
171+
}
172+
} else
173+
addConstValue(input->primaryValue()->value());
174+
175+
impl->block = previousBlock;
176+
}
177+
178+
break;
179+
}
180+
181+
case Input::Type::ObscuredShadow: {
182+
auto previousBlock = impl->block;
183+
impl->block = input->valueBlock();
184+
if (impl->block) {
185+
if (impl->block->compileFunction())
186+
impl->block->compile(this);
187+
else {
188+
std::cout << "warning: unsupported reporter block: " << impl->block->opcode() << std::endl;
189+
impl->unsupportedBlocks.insert(impl->block->opcode());
190+
addConstValue(Value());
191+
}
192+
} else
193+
input->primaryValue()->compile(this);
194+
195+
impl->block = previousBlock;
196+
break;
197+
}
198+
}
199+
}

0 commit comments

Comments
 (0)