Skip to content

LLVM: Refactor extension API #581

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Sep 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@ target_sources(scratchcpp
include/scratchcpp/scratchconfiguration.h
include/scratchcpp/iengine.h
include/scratchcpp/iextension.h
include/scratchcpp/iblocksection.h
include/scratchcpp/thread.h
include/scratchcpp/asset.h
include/scratchcpp/costume.h
Expand Down
158 changes: 0 additions & 158 deletions docs/Block sections.md

This file was deleted.

146 changes: 134 additions & 12 deletions docs/Extensions.md
Original file line number Diff line number Diff line change
@@ -1,30 +1,152 @@
\page extensions Extensions

An extension is a group of block sections (see [Block sections](blockSections.html)).
An extension is a group of Scratch blocks, for example motion blocks.

## Creating an extension
To create a custom extension, subclass \link libscratchcpp::IExtension IExtension \endlink and override
\link libscratchcpp::IExtension::name() name() \endlink and \link libscratchcpp::IExtension::description() description() \endlink
functions to return the name and the description of the extension.

There's also an \link libscratchcpp::IExtension::includeByDefault() includeByDefault() \endlink function which returns `true`
if the extension is intended to be hidden from the block palette and included in a Scratch project by default.

The default implementation returns `false`.
functions to return the name and the description of the extension. Then override \link libscratchcpp::IExtension::registerBlocks() registerBlocks() \endlink
where you'll register the compile functions for blocks.

It's recommended to use the libscratchcpp namespace like this in your extension class:
```cpp
using namespace libscratchcpp;
```

### Registering block sections
Block sections can be registered by overriding the \link libscratchcpp::IExtension::registerSections() registerSections() \endlink function:
### Adding blocks
See the [Scratch Wiki](https://en.scratch-wiki.info/wiki/Scratch_File_Format#Blocks) for more information about blocks.

Scratch projects are compiled by the \link libscratchcpp::Compiler Compiler \endlink class.
To add a block, you have to let the compiler know how to compile it.
Start by defining a **compile function**.
For example, the compile function for a `hello world` block would look like this:
```cpp
class MyExtension : public IExtension {
public:
...
static void compileHelloWorld(Compiler *compiler);
static unsigned int helloWorld(VirtualMachine *vm);
};

void MyExtension::compileHelloWorld(Compiler *compiler) {
compiler->addFunctionCall(&helloWorld);
}

unsigned int MyExtension::helloWorld(VirtualMachine *vm) {
std::cout << "Hello, world!" << std::endl;
return 0;
}
```
\note Make sure the functions are **static**.

Register the compile function using the \link libscratchcpp::IEngine::addCompileFunction() addCompileFunction() \endlink method in link libscratchcpp::IExtension::registerBlocks() registerBlocks() \endlink:
```cpp
MyExtension::registerBlocks(IEngine *engine) {
engine->addCompileFunction(this, "myextension_helloworld", &MyExtension::compileHelloWorld);
}
```
Where `myextension_helloworld` is the opcode of the `hello world` block.

### Adding inputs
To add inputs, create an `Inputs` enumeration in your extension:
```hpp
class MyExtension : public IExtension {
enum Inputs {
TEXT
};
...
};
```
Then add inputs in link libscratchcpp::IExtension::registerBlocks() registerBlocks() \endlink:
```cpp
MyExtension::registerBlocks(IEngine *engine) {
...
engine->addInput(this, "TEXT", TEXT);
}
```
The compiler will assign the input name with the `TEXT` ID. In this case, the ID is 0 because it's the first member of the enumeration.

To add the input to the compiled code, call the \link libscratchcpp::Compiler::addInput() addInput() \endlink function:
```cpp
void MyExtension::compileHelloWorld(Compiler *compiler) {
compiler->addInput(TEXT);
compiler->addFunctionCall(&helloWorld);
}
```

The value of the input can be read during runtime using the \link libscratchcpp::VirtualMachine::getInput() getInput() \endlink function:
```cpp
unsigned int MyExtension::helloWorld(VirtualMachine *vm) {
std::cout << "Hello, " << vm->getInput(0, 1)->toString() << "!" << std::endl;
return 1;
}
```
\note The order of the inputs is the same as in the compile function. Do not use the `Inputs` enumeration in runtime functions.

```cpp
vm->getInput(0, 1)
```
The first argument is the index of the input and the second argument is the amount of inputs.
\note Make sure to return the amount of inputs in the `helloWorld` function.

### Adding fields
**Fields** are drop-down menus into which one cannot drop a reporter.
Fields have a predefined set of values.
```cpp
class MyExtension : public IExtension {
...
enum Fields {
ANIMAL
};

enum FieldValues {
Cat,
Dog
};
...
};

MyExtension::registerBlocks(IEngine *engine) {
...
engine->addField(this, "ANIMAL", ANIMAL);
engine->addFieldValue(this, "Cat", Cat);
engine->addFieldValue(this, "Dog", Dog);
}
```

Because fields are handled at compile time, you can read them from the compile function:
```cpp
void MyExtension::registerSections(IEngine *engine) {
engine->registerSection(std::make_shared<MySection>());
void MyExtension::compileHelloWorld(Compiler *compiler) {
int id = compiler->field(ANIMAL)->specialValueId();

switch(id) {
case Cat:
compiler->addFunctionCall(&helloCat);
break;

case Dog:
compiler->addFunctionCall(&helloDog);
break;

default:
break;
}
}

unsigned int MyExtension::helloCat(VirtualMachine *vm) {
std::cout << "Hello, cat!" << std::endl;
return 0;
}

unsigned int MyExtension::helloDog(VirtualMachine *vm) {
std::cout << "Hello, dog!" << std::endl;
return 0;
}
```
See the [Block sections](blockSections.html) page for instructions on how to create a block section.
\note Don't confuse \link libscratchcpp::Field::specialValueId() specialValueId() \endlink with \link libscratchcpp::Field::valueId() valueId() \endlink
because \link libscratchcpp::Field::valueId() valueId() \endlink stores the ID of the block, variable, list or broadcast selected in the dropdown list.

To get a pointer to the block, variable, list or broadcast selected in the dropdown list, use \link libscratchcpp::Field::valuePtr() valuePtr() \endlink.

### Registering the extension
Register the extension **before** loading a project, using the \link libscratchcpp::ScratchConfiguration ScratchConfiguration \endlink class:
Expand Down
4 changes: 2 additions & 2 deletions docs/Getting started.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,5 +24,5 @@ int main(int argc, char **argv) {
The \link libscratchcpp::Project::run() run() \endlink method runs an event loop which stops after all scripts finish.

For CLI project players, using \link libscratchcpp::Project::run() run() \endlink is enough. If you are developing
a GUI project player and need to receive input events such as key presses, you'll need to use \link libscratchcpp::Project::runEventLoop() runEventLoop() \endlink
and run it in another thread (to keep the UI responsive).
a GUI project player and need to receive input events such as key presses, you'll need to use \link libscratchcpp::IEngine::step() step() \endlink
for ticks and \link libscratchcpp::Project::start() start() \endlink and \link libscratchcpp::Project::stop() stop() \endlink
33 changes: 0 additions & 33 deletions docs/Image formats.md

This file was deleted.

Loading
Loading