Skip to content

Commit

Permalink
Add ExtendingElectra.md
Browse files Browse the repository at this point in the history
Modify the signature of the work function of dynamic components
  • Loading branch information
DolphyWind committed Feb 17, 2024
1 parent 98b82b1 commit 869e211
Show file tree
Hide file tree
Showing 6 changed files with 78 additions and 6 deletions.
66 changes: 66 additions & 0 deletions ExtendingElectra.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
## Extending Electra Using C++
You can add your own components to electra using C++ shared libraries.
When the electra interpreter loads a shared library, it looks for two functions:
- `void load(ComponentInformation&)`
- `bool work(std::vector<std::stack<double>>&, Current::Ptr, std::vector<Current::Ptr>&)`

#### Load Function
The purpose of the `load` function is simple; It takes a reference to a ComponentInformation
object and modifies it. Let's look at an example:
```cpp
void load(ComponentInformation& c)
{
c.symbol = U'9';
c.directions = {Direction::EAST, Direction::WEST};
c.componentType = ComponentInformation::ComponentType::CLONING;
}
```
- The `symbol` field is the unicode character representation of the component. It overwrites
any component with the same symbol.
- The `directions` field is a list of supported directions.
- The `componentType` field specifies the component's type. The `componentType` can take
two values: `ComponentInformation::ComponentType::CLONING` or `ComponentInformation::ComponentType::NON_CLONING`.
`NON_CLONING` components do not clone the current after they've done their job like portals.
And as you may guess, `CLONING` components do clone the current after they've done their job like any other component in electra.
#### Work Function
The `work` function is the function specifies the actual job of the component. It has three parameters
- The first parameter is a reference to the memory of electra.
- The second parameter is a shared pointer to the current that triggered the component
- The third parameter is a vector of currents, if you want to create new currents, you can do so
by pushing new currents to this vector.
If the `work` function returns false, the current gets killed and no cloning occurs.
(Note: If you manually push some currents to the last parameter, they'll still be created)
### Full Example
Let's look at an example to better understand how you can create a custom component:
```cpp
#include "ComponentInformation.hpp"
#include "Current.hpp"
#include "Direction.hpp"
#include <iostream>
//g++ helloworld.cpp -fPIC -shared -o libhelloworld.so
extern "C"
{
// We create a custom component. It supports west and east directions and '5' is its symbol.
// It also clones currents after it's done its work
void load(ComponentInformation& c)
{
c.symbol = U'5';
c.directions = {Direction::EAST, Direction::WEST};
c.componentType = ComponentInformation::ComponentType::CLONING;
}
// The component just prints hello world to the screen
bool work(std::vector<std::stack<var_t>>& stacks, Current::Ptr currentPtr, std::vector<Current::Ptr>& currentVector)
{
std::cout << "hello world\n";
return true;
}
}
```
6 changes: 5 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ cmake --build . --config="Release"
Electra has three main components: Currents, Generators and Components. Electra uses a list of 64 stacks of doubles for its memory management.
(This implementation has a command line argument that lets you change the stack count)

## **Comments and Including Other Files**
## **Comments, Including Other Files and Extending Electra With C++**
In Electra, you can comment out your code using question marks.
```
? This is a comment ? >-+
Expand Down Expand Up @@ -86,6 +86,10 @@ To include other files in your code, use quotation marks.
"!foo.ec" 5:12 ? You can always do a force include ?
```

But be careful, files ending with `.dll`, `.so` or `.dylib` will be treated as dynamic components
(depends on your platform, on windows only the `.dll` files will be treated as dynamic components).
Dynamic components allow you to extend electra using C++. For more info, click [here](ExtendingElectra.md).

## **Currents**
Currents are instruction pointers in Electra. They all have a direction, a position, a stack that holds visited portals and a stack pointer.
A direction can take one of these eight values: East, Northeast, North, Northwest, West, Southwest, South, Southeast.
Expand Down
2 changes: 1 addition & 1 deletion examples/HQ9Plus_interpreter/99bottles.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ void load(ComponentInformation& c)
c.componentType = ComponentInformation::ComponentType::CLONING;
}

bool work(Current::Ptr currentPtr, std::vector<Current::Ptr>& currentVector)
bool work(std::vector<std::stack<var_t>>& stacks, Current::Ptr currentPtr, std::vector<Current::Ptr>& currentVector)
{

for(int i = 99; i > 1; --i)
Expand Down
2 changes: 1 addition & 1 deletion examples/HQ9Plus_interpreter/helloworld.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ void load(ComponentInformation& c)
c.componentType = ComponentInformation::ComponentType::CLONING;
}

bool work(Current::Ptr currentPtr, std::vector<Current::Ptr>& currentVector)
bool work(std::vector<std::stack<var_t>>& stacks, Current::Ptr currentPtr, std::vector<Current::Ptr>& currentVector)
{
std::cout << "hello world\n";
return true;
Expand Down
2 changes: 1 addition & 1 deletion examples/HQ9Plus_interpreter/hq9plus.ec
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"libhelloworld.so"

? Uses 4 stacks ?
? First three are for source code, the last one is for the accumulator ?
? First three are for the source code, the last one is for the accumulator ?

+-->---+ +------+
| & | |
Expand Down
6 changes: 4 additions & 2 deletions src/Electra.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -581,7 +581,8 @@ void Electra::loadDynamicComponent(const fs::path& path, const std::string& file
dylib lib(path.string(), filename, dylib::no_filename_decorations);
ComponentInformation componentInformation;
lib.get_function<void(ComponentInformation&)>("load")(componentInformation);
auto workFunc = lib.get_function<bool(Current::Ptr, std::vector<Current::Ptr>&)>("work");
auto workFuncWithStacksParam = lib.get_function<bool(std::vector<std::stack<var_t>>&, Current::Ptr, std::vector<Current::Ptr>&)>("work");
auto workFunc = std::bind(workFuncWithStacksParam, std::ref(m_stacks), std::placeholders::_1, std::placeholders::_2);
m_dynamicLibraries.push_back(std::move(lib));

if(componentInformation.componentType == ComponentInformation::ComponentType::NON_CLONING)
Expand All @@ -606,7 +607,8 @@ bool Electra::loadDynamicComponent(const std::string& filename)
dylib lib(filename, dylib::no_filename_decorations);
ComponentInformation componentInformation;
lib.get_function<void(ComponentInformation&)>("load")(componentInformation);
auto workFunc = lib.get_function<bool(Current::Ptr, std::vector<Current::Ptr>&)>("work");
auto workFuncWithStacksParam = lib.get_function<bool(std::vector<std::stack<var_t>>&, Current::Ptr, std::vector<Current::Ptr>&)>("work");
auto workFunc = std::bind(workFuncWithStacksParam, std::ref(m_stacks), std::placeholders::_1, std::placeholders::_2);
m_dynamicLibraries.push_back(std::move(lib));

if(componentInformation.componentType == ComponentInformation::ComponentType::NON_CLONING)
Expand Down

0 comments on commit 869e211

Please sign in to comment.