Skip to content

Commit b115f84

Browse files
committed
Add initial compiler
1 parent 89b7bbd commit b115f84

File tree

9 files changed

+265
-3
lines changed

9 files changed

+265
-3
lines changed

src/engine/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,13 @@ target_sources(scratchcpp
33
engine.cpp
44
blockargs.cpp
55
virtualmachine.cpp
6+
compiler.cpp
67
PUBLIC
78
engine.h
89
global.h
910
blockargs.h
1011
virtualmachine.h
12+
compiler.h
1113
iblocksection.h
1214
iextension.h
1315
)

src/engine/compiler.cpp

Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
1+
// SPDX-License-Identifier: Apache-2.0
2+
3+
#include "compiler.h"
4+
#include "engine.h"
5+
6+
using namespace libscratchcpp;
7+
using namespace vm;
8+
9+
/*! Constructs Compiler. */
10+
Compiler::Compiler(Engine *engine, std::shared_ptr<Block> topLevelBlock) :
11+
m_engine(engine),
12+
m_block(topLevelBlock)
13+
{
14+
}
15+
16+
/*! Compiles the script. Use bytecode() to read the generated bytecode. */
17+
void Compiler::compile()
18+
{
19+
// Add start instruction
20+
addInstruction(OP_START);
21+
22+
m_block = m_block->next();
23+
while (m_block) {
24+
if (m_block->compileFunction())
25+
m_block->compile(this);
26+
else
27+
std::cout << "warning: unsupported block: " << m_block->opcode() << std::endl;
28+
m_block = m_block->next();
29+
}
30+
31+
// Add end instruction (halt)
32+
addInstruction(OP_HALT);
33+
}
34+
35+
/*! Returns the generated bytecode. */
36+
const std::vector<unsigned int> &Compiler::bytecode() const
37+
{
38+
return m_bytecode;
39+
}
40+
41+
/*! Returns the list of constant input values. */
42+
const std::vector<InputValue *> &Compiler::constInputValues() const
43+
{
44+
return m_constValues;
45+
}
46+
47+
/*! Returns the list of constant values. */
48+
std::vector<Value> Compiler::constValues() const
49+
{
50+
std::vector<Value> ret;
51+
for (auto value : m_constValues)
52+
ret.push_back(value->value());
53+
return ret;
54+
}
55+
56+
/*! Sets the list of constant input values. */
57+
void Compiler::setConstInputValues(const std::vector<InputValue *> &values)
58+
{
59+
m_constValues = values;
60+
}
61+
62+
/*! Returns the list of variables. */
63+
const std::vector<Variable *> &Compiler::variables() const
64+
{
65+
return m_variables;
66+
}
67+
68+
/*! Returns the list of pointers to variable values. */
69+
std::vector<Value *> Compiler::variablePtrs() const
70+
{
71+
std::vector<Value *> ret;
72+
for (auto var : m_variables)
73+
ret.push_back(var->valuePtr());
74+
return ret;
75+
}
76+
77+
/*! Sets the list of variables. */
78+
void Compiler::setVariables(std::vector<Variable *> variables)
79+
{
80+
m_variables = variables;
81+
}
82+
83+
/*! Returns the list of lists. */
84+
const std::vector<List *> &Compiler::lists() const
85+
{
86+
return m_lists;
87+
}
88+
89+
/*! Sets the list of lists. */
90+
void Compiler::setLists(std::vector<List *> lists)
91+
{
92+
m_lists = lists;
93+
}
94+
95+
/*! Adds an instruction to the bytecode. */
96+
void Compiler::addInstruction(Opcode opcode, std::initializer_list<unsigned int> args)
97+
{
98+
m_bytecode.push_back(opcode);
99+
for (auto arg : args)
100+
m_bytecode.push_back(arg);
101+
}
102+
103+
/*! Compiles the given input and adds it to the bytecode. */
104+
void Compiler::addInput(Input *input)
105+
{
106+
switch (input->type()) {
107+
case Input::Type::Shadow:
108+
addInstruction(OP_CONST, { constIndex(input->primaryValue()) });
109+
break;
110+
111+
case Input::Type::NoShadow:
112+
addInstruction(OP_NULL);
113+
break;
114+
115+
case Input::Type::ObscuredShadow:
116+
// TODO
117+
break;
118+
}
119+
}
120+
121+
/*! Returns the input with the given ID. */
122+
Input *Compiler::input(int id) const
123+
{
124+
return m_block->findInputById(id);
125+
}
126+
127+
/*! Returns the field with the given ID. */
128+
Field *Compiler::field(int id) const
129+
{
130+
return m_block->findFieldById(id);
131+
}
132+
133+
/*! Returns the index of the given variable. */
134+
unsigned int Compiler::variableIndex(std::shared_ptr<IEntity> varEntity)
135+
{
136+
auto var = dynamic_cast<Variable *>(varEntity.get());
137+
auto it = std::find(m_variables.begin(), m_variables.end(), var);
138+
if (it != m_variables.end())
139+
return it - m_variables.begin();
140+
m_variables.push_back(var);
141+
return m_variables.size() - 1;
142+
}
143+
144+
unsigned int Compiler::constIndex(InputValue *value)
145+
{
146+
auto it = std::find(m_constValues.begin(), m_constValues.end(), value);
147+
if (it != m_constValues.end())
148+
return it - m_constValues.begin();
149+
m_constValues.push_back(value);
150+
return m_constValues.size() - 1;
151+
}

src/engine/compiler.h

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
// SPDX-License-Identifier: Apache-2.0
2+
3+
#pragma once
4+
5+
#include "virtualmachine.h"
6+
#include <vector>
7+
8+
namespace libscratchcpp
9+
{
10+
11+
/*! \brief The Compiler class provides an API for compiling Scratch scripts to bytecode. */
12+
class LIBSCRATCHCPP_EXPORT Compiler
13+
{
14+
public:
15+
Compiler(Engine *engine, std::shared_ptr<Block> topLevelBlock);
16+
Compiler(const Compiler &) = delete;
17+
18+
void compile();
19+
20+
const std::vector<unsigned int> &bytecode() const;
21+
22+
const std::vector<InputValue *> &constInputValues() const;
23+
std::vector<Value> constValues() const;
24+
void setConstInputValues(const std::vector<InputValue *> &values);
25+
26+
const std::vector<Variable *> &variables() const;
27+
std::vector<Value *> variablePtrs() const;
28+
void setVariables(std::vector<Variable *> variables);
29+
30+
const std::vector<List *> &lists() const;
31+
void setLists(std::vector<List *> lists);
32+
33+
void addInstruction(vm::Opcode opcode, std::initializer_list<unsigned int> args = {});
34+
void addInput(Input *input);
35+
36+
Input *input(int id) const;
37+
Field *field(int id) const;
38+
unsigned int variableIndex(std::shared_ptr<IEntity> varEntity);
39+
40+
private:
41+
unsigned int constIndex(InputValue *value);
42+
43+
Engine *m_engine;
44+
std::shared_ptr<Block>(m_block);
45+
46+
std::vector<unsigned int> m_bytecode;
47+
std::vector<InputValue *> m_constValues;
48+
std::vector<Variable *> m_variables;
49+
std::vector<List *> m_lists;
50+
};
51+
52+
} // namespace libscratchcpp

src/engine/engine.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,10 @@ void Engine::compile()
3838
auto section = blockSection(block->opcode());
3939
block->setNext(getBlock(block->nextId()));
4040
block->setParent(getBlock(block->parentId()));
41-
if (section)
41+
if (section) {
4242
block->setImplementation(section->resolveBlock(block->opcode()));
43+
block->setCompileFunction(section->resolveBlockCompileFunc(block->opcode()));
44+
}
4345

4446
auto inputs = block->inputs();
4547
for (auto input : inputs) {

src/engine/global.h

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

1313
class LIBSCRATCHCPP_EXPORT VirtualMachine;
14+
class LIBSCRATCHCPP_EXPORT Compiler;
1415

1516
/*!
1617
* \typedef BlockImpl
@@ -26,6 +27,13 @@ using BlockImpl = Value (*)(const BlockArgs &);
2627
*/
2728
using BlockFunc = unsigned int (*)(VirtualMachine *vm);
2829

30+
/*!
31+
* \typedef BlockComp
32+
*
33+
* BlockComp is a function pointer for functions which are used to compile blocks to bytecode.
34+
*/
35+
using BlockComp = void (*)(Compiler *);
36+
2937
/*! Generates a random number in the given interval like the random.randint() function in Python. */
3038
template<typename T>
3139
inline T randint(T start, T end)

src/engine/iblocksection.h

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,17 @@ class IBlockSection
3838
return nullptr;
3939
}
4040

41+
/*!
42+
* Returns a pointer to the compile function of the given block opcode.\n
43+
* Used internally by Engine.
44+
*/
45+
virtual BlockComp resolveBlockCompileFunc(const std::string &opcode) const final
46+
{
47+
if (m_compileFunctions.count(opcode) == 1)
48+
return m_compileFunctions.at(opcode);
49+
return nullptr;
50+
}
51+
4152
/*!
4253
* Returns the ID of the input with the given name.\n
4354
* Used internally by Engine.
@@ -83,6 +94,17 @@ class IBlockSection
8394
m_blocks[opcode] = f;
8495
}
8596

97+
/*!
98+
* Assigns a compile function pointer to a block opcode.
99+
* \param[in] opcode The block opcode
100+
* \param[in] f A pointer to the compile function
101+
*/
102+
template<class F>
103+
void addCompileFunction(const std::string &opcode, F &&f)
104+
{
105+
m_compileFunctions[opcode] = f;
106+
}
107+
86108
/*! Assigns an input ID to an input name. */
87109
virtual void addInput(const std::string &name, int id) final { m_inputs[name] = id; }
88110

@@ -94,6 +116,7 @@ class IBlockSection
94116

95117
private:
96118
std::map<std::string, BlockImpl> m_blocks;
119+
std::map<std::string, BlockComp> m_compileFunctions;
97120
std::map<std::string, int> m_inputs;
98121
std::map<std::string, int> m_fields;
99122
std::map<std::string, int> m_fieldValues;

src/scratch/block.cpp

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,11 +23,15 @@ Value Block::run(RunningScript *script, const Value &defaultRetValue)
2323
{
2424
if (m_implementation)
2525
return m_implementation(BlockArgs(m_target, m_engine, script, this));
26-
else
27-
std::cout << "warning: unsupported block: " << m_opcode << std::endl;
2826
return defaultRetValue;
2927
}
3028

29+
/*! Calls the compile function. */
30+
void Block::compile(Compiler *compiler)
31+
{
32+
return m_compileFunction(compiler);
33+
}
34+
3135
/*! Returns the opcode. */
3236
std::string Block::opcode() const
3337
{
@@ -46,6 +50,18 @@ void Block::setImplementation(BlockImpl impl)
4650
m_implementation = impl;
4751
}
4852

53+
/*! Returns the compile function. \see <a href="blockSections.html">Block sections</a> */
54+
BlockComp Block::compileFunction() const
55+
{
56+
return m_compileFunction;
57+
}
58+
59+
/*! Sets the compile function. \see <a href="blockSections.html">Block sections</a> */
60+
void Block::setCompileFunction(BlockComp newCompileFunction)
61+
{
62+
m_compileFunction = newCompileFunction;
63+
}
64+
4965
/*! Returns the next block. */
5066
std::shared_ptr<Block> Block::next() const
5167
{

src/scratch/block.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ class LIBSCRATCHCPP_EXPORT Block : public IEntity
2121
Block(const Block &) = delete;
2222

2323
Value run(RunningScript *script = nullptr, const Value &defaultRetValue = Value());
24+
void compile(Compiler *compiler);
2425

2526
std::string opcode() const;
2627

@@ -61,9 +62,13 @@ class LIBSCRATCHCPP_EXPORT Block : public IEntity
6162

6263
void setTarget(Target *newTarget);
6364

65+
BlockComp compileFunction() const;
66+
void setCompileFunction(BlockComp newCompileFunction);
67+
6468
private:
6569
std::string m_opcode;
6670
BlockImpl m_implementation = nullptr;
71+
BlockComp m_compileFunction = nullptr;
6772
std::shared_ptr<Block> m_next = nullptr;
6873
std::string m_nextId;
6974
std::shared_ptr<Block> m_parent = nullptr;

src/scratch/variable.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,9 @@ class LIBSCRATCHCPP_EXPORT Variable : public IEntity
2222
/*! Returns the value. */
2323
inline const Value &value() const { return m_value; }
2424

25+
/*! Returns a pointer to the value. */
26+
inline Value *valuePtr() { return &m_value; }
27+
2528
/*! Sets the value. */
2629
inline void setValue(const Value &value) { m_value = value; }
2730

0 commit comments

Comments
 (0)