Compare commits

...

3 commits

Author SHA1 Message Date
anthony@rabine.fr
51eac85360 make story editor build
Some checks are pending
Build-StoryEditor / build_linux (push) Waiting to run
Build-StoryEditor / build_win32 (push) Waiting to run
Deploy-Documentation / deploy (push) Waiting to run
2025-04-23 00:01:09 +02:00
anthony@rabine.fr
a42fdc81ea First working assembly from nodes (cli), youpi 2025-04-22 23:20:44 +02:00
anthony@rabine.fr
3c1224e937 Better custom AST builder using kahn algorithm 2025-04-22 16:35:50 +02:00
40 changed files with 863 additions and 902 deletions

View file

@ -109,7 +109,8 @@
"text_encoding": "cpp",
"serializers.h": "c",
"ni_parser.h": "c",
"*.m": "cpp"
"*.m": "cpp",
"*.inc": "cpp"
}
}

View file

@ -83,9 +83,9 @@ typedef enum
OP_SKIPNZ = 27, ///< skip next instruction if not zero, e.g.: skipnz r2
// Comparison
OP_CMP_EQ = 28, ///< compare two registers for equality, result in first e.g.: cmp_eq r4, r0, r1 (r4 = (r0 == r1 ? 1 : 0)
OP_CMP_GT = 29, ///< compare if first register is greater than the second, result in first e.g.: cmp_gt r4, r0, r1
OP_CMP_LT = 30, ///< compare if first register is less than the second, result in first e.g.: cmp_lt r4, r0, r1
OP_CMP_EQ = 28, ///< compare two registers for equality, result in first e.g.: eq r4, r0, r1 (r4 = (r0 == r1 ? 1 : 0)
OP_CMP_GT = 29, ///< compare if first register is greater than the second, result in first e.g.: gt r4, r0, r1
OP_CMP_LT = 30, ///< compare if first register is less than the second, result in first e.g.: lt r4, r0, r1
INSTRUCTION_COUNT
} chip32_instruction_t;

View file

@ -36,7 +36,6 @@ Purpose: grammar, ram usage and macros, rom code generation
void hexdump(void *ptr, int buflen);
static const std::string test1 = R"(; jump over the data, to our entry label
jump .entry
$imageBird DC8 "example.bmp", 8 ; data
$someConstant DC32 12456789
@ -46,7 +45,7 @@ $RamData1 DV32 1 ; one 32-bit integer
$MyArray DV8 10 ; array of 10 bytes
; label definition
.entry: ;; comment here should work
.main: ;; comment here should work
; We create a stupid loop just for RAM variable testing
lcons r0, 4 ; prepare loop: 4 iterations
@ -183,14 +182,12 @@ static const std::string testPrintf = R"(
; ========================================================
; We test the printf system call
; ========================================================
jump .entry
$printHello DC8 "La réponse est %d"
$answer DC32 42
$counter DV32 10
.entry:
.main:
; prepapre loop

View file

@ -56,7 +56,7 @@ public:
virtual uint32_t GetRegister(int reg) = 0;
// Variables management
virtual void ScanVariable(const std::function<void(Variable& element)>& operation) = 0;
virtual void ScanVariable(const std::function<void(std::shared_ptr<Variable> element)>& operation) = 0;
virtual void AddVariable() = 0;
virtual void DeleteVariable(int i) = 0;

View file

@ -2,19 +2,219 @@
#include <string>
#include <cstdint>
#include <variant>
struct Variable {
static const uint32_t NameMaxSize = 50;
std::string name; // Nom de la variable
std::string type; // Type de la variable (par exemple int32_t, int64_t)
int64_t value; // Valeur stockée en tant qu'entier en virgule fixe
std::string valueText;
int scalePower; // Nombre de bits pour la partie fractionnaire
#include "uuid.h"
Variable(const std::string &n, const std::string &t, int64_t v, int s) {
name = n;
type = t;
value = v;
scalePower = s;
class Variable
{
public:
enum class ValueType {
INTEGER,
FLOAT,
BOOL,
STRING
};
enum RandomFlags
{
CHARSET_ALPHABET_LOWER = 0x1, // "abcdefghijklmnopqrstuvwxyz"
CHARSET_ALPHABET_UPPER = 0x2, // "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
CHARSET_NUMBERS = 0x4, // "0123456789"
CHARSET_SIGNS = 0x8, // "!@#$%^&*()_+-=[]{}|;:,.<>?";
ALL_CHARSETS = CHARSET_ALPHABET_LOWER | CHARSET_ALPHABET_UPPER |CHARSET_NUMBERS | CHARSET_SIGNS
};
static const int NameMaxSize = 32; // Max size for the variable name
Variable() {
m_uuid = Uuid().String();
m_label = Variable::GenerateRandomString(10, Variable::CHARSET_ALPHABET_LOWER | Variable::CHARSET_ALPHABET_UPPER );
}
Variable (const std::string &name)
: Variable()
{
m_variableName = name;
}
// Setters
void SetUuid(const std::string& uuid) {
m_uuid = uuid;
}
void SetVariableName(const std::string& name) {
m_variableName = name;
}
void SetConstant(bool isConstant) {
m_isConstant = isConstant;
}
void SetValueType(ValueType type) {
m_valueType = type;
// Reset value to default for new type
switch (type) {
case ValueType::INTEGER:
m_value = 0;
break;
case ValueType::FLOAT:
m_value = 0.0f;
break;
case ValueType::BOOL:
m_value = false;
break;
case ValueType::STRING:
m_value = "";
break;
}
}
template<typename T>
void SetValue(const T& value) {
try {
m_value = value;
} catch (const std::bad_variant_access&) {
throw std::runtime_error("[variable.h] SetValue(): Invalid value type for variable");
}
}
void SetTextValue(const std::string& value) {
SetValue<std::string>(value);
m_valueType = ValueType::STRING;
}
void SetIntegerValue(int value) {
SetValue<int>(value);
m_valueType = ValueType::INTEGER;
}
void SetFloatValue(float value) {
SetValue<float>(value);
m_valueType = ValueType::FLOAT;
}
void SetBoolValue(bool value) {
SetValue<bool>(value);
m_valueType = ValueType::BOOL;
}
void SetScalePower(int scalePower) {
m_scalePower = scalePower;
}
// Getters
std::string GetVariableName() const {
return m_variableName;
}
bool IsConstant() const {
return m_isConstant;
}
ValueType GetValueType() const {
return m_valueType;
}
std::string GetLabel() const {
return m_label;
}
template<typename T>
T GetValue() const {
try {
return std::get<T>(m_value);
} catch (const std::bad_variant_access&) {
throw std::runtime_error("[variable.h] GetValue(): Invalid value type requested");
}
}
std::string GetStringValue() const {
return GetValue<std::string>();
}
int GetIntegerValue() const {
return GetValue<int>();
}
float GetFloatValue() const {
return GetValue<float>();
}
bool GetBoolValue() const {
return GetValue<bool>();
}
using VariableValue = std::variant<int, float, bool, std::string>;
std::string GetUuid() const {
return m_uuid;
}
bool IsString() const {
return m_valueType == ValueType::STRING;
}
bool IsInteger() const {
return m_valueType == ValueType::INTEGER;
}
bool IsFloat() const {
return m_valueType == ValueType::FLOAT;
}
bool IsBool() const {
return m_valueType == ValueType::BOOL;
}
int GetScalePower() const {
return m_scalePower;
}
static std::string GenerateRandomString(size_t length, uint32_t flags)
{
std::string charset = "";
if (flags & CHARSET_ALPHABET_LOWER)
{
charset += "abcdefghijklmnopqrstuvwxyz";
}
if (flags & CHARSET_ALPHABET_UPPER)
{
charset += "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
}
if (flags & CHARSET_NUMBERS)
{
charset += "0123456789";
}
if (flags & CHARSET_SIGNS)
{
charset += "!@#$%^&*()_+-=[]{}|;:,.<>?";
}
std::random_device rd;
std::mt19937 generator(rd());
std::uniform_int_distribution<> distribution(0, charset.size() - 1);
std::string result;
result.reserve(length);
for (size_t i = 0; i < length; ++i) {
result += charset[distribution(generator)];
}
return result;
}
private:
std::string m_variableName; // nom humain
ValueType m_valueType;
VariableValue m_value;
bool m_isConstant;
std::string m_uuid; // pour identifier le variable dans le JSON du projet
std::string m_label; // pour la génération assembleur
int m_scalePower; // Nombre de bits pour la partie fractionnaire
};

View file

@ -15,12 +15,10 @@
#include "variable_node.h"
#include "branch_node.h"
#include "operator_node.h"
#include "base_node.h"
class AssemblyGenerator {
class AssemblyGenerator : public IVariableVisitor {
public:
struct GeneratorContext {
std::string timestamp;
@ -58,9 +56,8 @@ public:
m_currentSection = Section::NONE;
}
std::string GenerateAssembly(std::vector<std::shared_ptr<BaseNode>> &nodes, const std::vector<PathTree>& roots)
std::string GenerateAssembly(std::vector<std::shared_ptr<BaseNode>> &nodes, const std::vector<std::shared_ptr<ASTNode>>& order, const std::vector<std::shared_ptr<Variable>> &variables)
{
m_roots = roots;
Reset();
// Generate header comments
@ -68,11 +65,11 @@ public:
// Generate data section
StartSection(Section::DATA);
GenerateDataSection(nodes, roots);
GenerateDataSection(nodes, variables);
// Generate text section
StartSection(Section::TEXT);
GenerateTextSection(roots);
GenerateTextSection(order);
return m_assembly.str();
}
@ -90,6 +87,8 @@ protected:
virtual void GenerateExit() = 0;
virtual void GenerateVariable(const std::shared_ptr<Variable> v) = 0;
std::string AddStringLiteral(const std::string& text)
{
@ -112,7 +111,7 @@ protected:
std::vector<std::string> m_stringLiterals;
int m_depth{0};
Section m_currentSection;
std::vector<PathTree> m_roots;
private:
@ -139,59 +138,47 @@ private:
}
}
void GenerateDataSection(const std::vector<std::shared_ptr<BaseNode>> &nodes, const std::vector<PathTree>& trees) {
void GenerateDataSection(const std::vector<std::shared_ptr<BaseNode>> &nodes, const std::vector<std::shared_ptr<Variable>> &variables)
{
// Generate all constants
for (const auto& n : nodes) {
m_assembly << n->GenerateConstants();
n->Accept(*this);
}
// Generate string literals
for (const auto& literal : m_stringLiterals) {
std::string label = "str_" + std::to_string(m_labelCounter++);
m_assembly << label << " db '" << literal << "',0\n"
<< label << "_len equ $ - " << label << "\n";
}
// Generate variables
for (const auto& t : trees) {
CollectVariables(t.root);
// generate all variables in RAM
for (auto & v : variables)
{
GenerateVariable(v);
}
m_assembly << "\n\n";
}
void GenerateTextSection(const std::vector<PathTree>& trees) {
void GenerateTextSection(const std::vector<std::shared_ptr<ASTNode>>& orderedNodes) {
// Program entry point
m_assembly << ".main:\n";
// Process execution paths first
for (const auto& tree : trees) {
if (tree.isExecutionPath) {
GenerateNodeCode(tree.root);
}
}
// Process data paths
for (const auto& tree : trees) {
if (!tree.isExecutionPath) {
GenerateNodeCode(tree.root, true);
}
for (const auto& node : orderedNodes) {
GenerateNodeCode(node);
}
// Program exit
GenerateExit();
}
void CollectVariables(std::shared_ptr<ASTNode> node) {
void CollectVariables(std::shared_ptr<ASTNode> node)
{
if (!node) return;
if (node->IsType<VariableNode>()) {
auto* varNode = node->GetAs<VariableNode>();
std::string varName = varNode->GetVariableName();
/*
std::string varName = varNode->GetN();
if (m_variableAddresses.find(varName) == m_variableAddresses.end()) {
m_variableAddresses[varName] = varName;
m_assembly << varName << ":\n" << varNode->GenerateAssembly();
}
*/
}
// Traverse children

View file

@ -21,38 +21,30 @@ public:
if (m_context.debugOutput) {
AddComment("Node: " + node->node->GetTypeName() + " (ID: " + node->node->GetId() + ")");
}
// Node label
m_assembly << node->node->GetMyEntryLabel() << ":\n";
if (isDataPath)
{
if (node->IsType<OperatorNode>()) {
GenerateOperatorNode(node);
}
else if (node->IsType<VariableNode>()) {
GenerateVariableNode(node);
}
if (node->IsType<OperatorNode>()) {
GenerateOperatorNode(node);
}
else
{
if (node->IsType<FunctionEntryNode>()) {
GenerateFunctionEntry(node);
}
else if (node->IsType<BranchNode>()) {
GenerateBranchNode(node);
}
else if (node->IsType<PrintNode>()) {
GeneratePrintNode(node);
}
else if (node->IsType<FunctionEntryNode>()) {
GenerateFunctionEntry(node);
}
else if (node->IsType<BranchNode>()) {
GenerateBranchNode(node);
}
else if (node->IsType<PrintNode>()) {
GeneratePrintNode(node);
}
// If we're processing a data path, traverse data outputs
if (isDataPath) {
for (const auto& [port, outputs] : node->dataOutputs) {
for (const auto& target : outputs) {
GenerateNodeCode(target.node, true);
}
}
}
// // If we're processing a data path, traverse data outputs
// if (isDataPath) {
// for (const auto& [port, outputs] : node->dataOutputs) {
// for (const auto& target : outputs) {
// GenerateNodeCode(target.node, true);
// }
// }
// }
}
@ -60,8 +52,6 @@ public:
m_assembly << std::string(m_depth * 4, ' ') << "; " << comment << "\n";
}
virtual void GenerateExit() {
AddComment("Program exit");
m_assembly << " halt\n";
@ -71,55 +61,22 @@ private:
void GenerateFunctionEntry(std::shared_ptr<ASTNode> node) {
AddComment("Function Entry");
m_depth++;
for (auto& child : node->children) {
GenerateNodeCode(child);
}
m_depth--;
}
void GenerateBranchNode(std::shared_ptr<ASTNode> node)
{
std::string labelTrue = GenerateUniqueLabel("true");
std::string labelFalse = GenerateUniqueLabel("false");
std::string labelEnd = GenerateUniqueLabel("end");
AddComment("Branch condition evaluation");
m_depth++;
// Generate condition code
// We search a path tree that have a last node equivalent to our node
// (this is the input of the condition)
auto lastNode = std::find_if(m_roots.begin(), m_roots.end(),
[&node](const PathTree& tree) {
return tree.lastNode && tree.lastNode->node->GetId() == node->node->GetId();
});
AddComment("Last node: " + lastNode->lastNode->node->GetTypeName() + " (ID: " + lastNode->lastNode->node->GetId() + ")");
auto trueBranch = node->GetChild(0);
auto falseBranch = node->GetChild(1);
// Compare result and jump
m_assembly << " pop eax\n"
<< " cmp eax, 0\n"
<< " je " << labelFalse << "\n";
m_assembly << " pop r0\n"
<< " skipz r0\n"
<< " jump " << trueBranch->node->GetMyEntryLabel() << "\n"
<< " jump " << falseBranch->node->GetMyEntryLabel() << "\n";
// True branch
m_assembly << labelTrue << ":\n";
if (node->GetChildCount() > 0) {
GenerateNodeCode(node->GetChild(0));
}
m_assembly << " jmp " << labelEnd << "\n";
// False branch
m_assembly << labelFalse << ":\n";
if (node->GetChildCount() > 1) {
GenerateNodeCode(node->GetChild(1));
}
m_assembly << labelEnd << ":\n";
m_depth--;
}
@ -127,15 +84,19 @@ private:
auto* printNode = node->GetAs<PrintNode>();
if (!printNode) return;
std::string text = printNode->GetText();
std::string label = AddStringLiteral(text);
std::string label = printNode->GetLabel();
m_assembly << " push r0\n"
<< " push r1\n"
<< " lcons r0, $" << label << "\n"
<< " lcons r1, 0 ; number of arguments\n" // FIXME: handle arguments
<< " syscall 4\n"
<< " pop r1\n"
<< " pop r0\n";
// << ""mov r2, %2 // arguments are in r2, r3, r4 etc.
// System call to write
m_assembly << " mov edx, " << label << "_len\n"
<< " mov ecx, " << label << "\n"
<< " mov ebx, 1\n" // stdout
<< " mov eax, 4\n" // sys_write
<< " int 0x80\n";
}
void GenerateOperatorNode(std::shared_ptr<ASTNode> node) {
@ -145,44 +106,123 @@ private:
AddComment("Operator: " + std::to_string(static_cast<int>(opNode->GetOperationType())));
m_depth++;
// Generate code for operands
for (const auto& [port, inputNode] : node->dataInputs) {
GenerateNodeCode(inputNode);
}
// Generate code for variables usage
int reg = 0;
for (const auto& [port, inputNode] : node->dataInputs)
{
// Check if the input node is a variable
if (inputNode->IsType<VariableNode>())
{
auto* varNode = inputNode->GetAs<VariableNode>();
if (varNode) {
auto var = varNode->GetVariable();
// Generate code to load the variable value
// FIXME: hardcoded 4 bytes, replace by actual real variable size
m_assembly << " load r" << reg << ", $" << var->GetLabel() << ", 4" << "; Load variable " << var->GetVariableName() << "\n";
m_assembly << " push r" << reg << "\n";
// Assuming we have a function to load the variable value
// m_assembly << " load r0, " << varNode->GetVariableName() << "\n";
}
reg++;
}
// m_assembly << " load r0, " << inputNode.node->GetId() << "\n";
}
// Generate operator code
switch (opNode->GetOperationType()) {
case OperatorNode::OperationType::ADD:
m_assembly << " pop ebx\n"
<< " pop eax\n"
<< " add eax, ebx\n"
<< " push eax\n";
m_assembly << " pop r0\n"
<< " pop r1\n"
<< " add r0, r1\n"
<< " push r0\n";
break;
case OperatorNode::OperationType::SUBTRACT:
m_assembly << " pop ebx\n"
<< " pop eax\n"
<< " sub eax, ebx\n"
<< " push eax\n";
m_assembly << " pop r0\n"
<< " pop r1\n"
<< " sub r0, r1\n"
<< " push r0\n";
break;
case OperatorNode::OperationType::MULTIPLY:
m_assembly << " pop ebx\n"
<< " pop eax\n"
<< " imul eax, ebx\n"
<< " push eax\n";
m_assembly << " pop r0\n"
<< " pop r1\n"
<< " mul r0, r1\n"
<< " push r0\n";
break;
case OperatorNode::OperationType::DIVIDE:
m_assembly << " pop ebx\n"
<< " pop eax\n"
<< " cdq\n"
<< " idiv ebx\n"
<< " push eax\n";
m_assembly << " pop r0\n"
<< " pop r1\n"
<< " div r0, r1\n"
<< " push r0\n";
break;
case OperatorNode::OperationType::AND:
m_assembly << " pop r0\n"
<< " pop r1\n"
<< " and r0, r1\n"
<< " push r0\n";
break;
case OperatorNode::OperationType::GREATER_THAN:
m_assembly << " pop r0\n"
<< " pop r1\n"
<< " gt r0, r0, r1\n"
<< " push r0\n";
break;
default:
// Make voluntary bad assembly
m_assembly << "------>>>> OPERATOR NOT IMPLEMENTED: " << opNode->GetOperatorSymbol() << "\n";
break;
// Add other operators...
}
m_depth--;
}
virtual void Visit(const std::shared_ptr<Variable> v) override
{
if (v->IsConstant())
{
if (v->GetValueType() == Variable::ValueType::STRING)
{
m_assembly << "$" << v->GetLabel() << " DC8, \"" << v->GetValue<std::string>() << "\" ; " << v->GetVariableName() << "\n";
}
else if (v->GetValueType() == Variable::ValueType::INTEGER)
{
m_assembly << "$" << v->GetLabel() << " DC32, " << v->GetValue<int>() << " ; " << v->GetVariableName() << "\n";
}
else if (v->GetValueType() == Variable::ValueType::FLOAT)
{
m_assembly << "$" << v->GetLabel() << " DC32, " << v->GetValue<float>() << " ; " << v->GetVariableName() << "\n";
}
else if (v->GetValueType() == Variable::ValueType::BOOL)
{
m_assembly << "$" << v->GetLabel() << " DCB, " << (v->GetValue<bool>() ? "1" : "0") << " ; " << v->GetVariableName() << "\n";
}
}
}
virtual void GenerateVariable(const std::shared_ptr<Variable> v)
{
if (v->GetValueType() == Variable::ValueType::STRING)
{
m_assembly << "$" << v->GetLabel() << " DV8, \"" << v->GetValue<std::string>() << "\" ; " << v->GetVariableName() << "\n";
}
else if (v->GetValueType() == Variable::ValueType::INTEGER)
{
m_assembly << "$" << v->GetLabel() << " DV32, " << v->GetValue<int>() << " ; " << v->GetVariableName() << "\n";
}
else if (v->GetValueType() == Variable::ValueType::FLOAT)
{
m_assembly << "$" << v->GetLabel() << " DV32, " << v->GetValue<float>() << " ; " << v->GetVariableName() << "\n";
}
else if (v->GetValueType() == Variable::ValueType::BOOL)
{
m_assembly << "$" << v->GetLabel() << " DVB, " << (v->GetValue<bool>() ? "1" : "0") << " ; " << v->GetVariableName() << "\n";
}
}
/*
void GenerateVariableNode(std::shared_ptr<ASTNode> node) {
auto* varNode = node->GetAs<VariableNode>();
if (!varNode) return;
@ -193,7 +233,7 @@ private:
m_assembly << " mov eax, [" << m_variableAddresses[varName] << "]\n"
<< " push eax\n";
}
*/
};

View file

@ -107,6 +107,14 @@ public:
return dynamic_cast<T*>(node.get());
}
std::string GetId() const {
return node->GetId();
}
int GetWeight() const {
return node->GetWeight();
}
// Debug information
std::string GetDebugString() const {
std::string result = "Node: " + node->GetTypeName() + " (ID: " + node->GetId() + ")\n";
@ -136,12 +144,6 @@ public:
}
};
struct PathTree {
std::shared_ptr<ASTNode> root;
std::shared_ptr<ASTNode> lastNode; // Dernier nœud du PathTree
std::vector<std::shared_ptr<Connection>> connections;
bool isExecutionPath; // true for main flow, false for input paths
};
class ASTBuilder {
public:
@ -150,88 +152,161 @@ public:
const std::vector<std::shared_ptr<Connection>>& connections)
: m_nodes(nodes), m_connections(connections) {}
std::vector<PathTree> BuildAST() {
std::vector<std::shared_ptr<ASTNode>> BuildAST()
{
// Create node map for quick lookups
std::unordered_map<std::string, std::shared_ptr<BaseNode>> nodeMap;
std::unordered_map<std::string, std::shared_ptr<ASTNode>> nodeMap;
for (const auto& node : m_nodes) {
nodeMap[node->GetId()] = node;
nodeMap[node->GetId()] = std::make_shared<ASTNode>(node);
}
// Find all root nodes (nodes without incoming execution connections)
std::unordered_set<std::string> hasIncomingExec;
std::unordered_set<std::string> hasIncomingData;
// Build adjacency list for the nodes from the connections
for (const auto& conn : m_connections) {
if (conn->type == Connection::EXECUTION_LINKJ) {
hasIncomingExec.insert(conn->inNodeId);
} else {
hasIncomingData.insert(conn->inNodeId);
// Don't add the variables nodes, as they are input data nodes.
auto rawNode = nodeMap[conn->outNodeId].get()->node;
if (dynamic_cast<VariableNode*>(rawNode.get())) {
continue;
}
m_adjList[conn->outNodeId].push_back(conn->inNodeId);
}
// Collect root nodes
std::vector<std::shared_ptr<BaseNode>> execRoots;
std::vector<std::shared_ptr<BaseNode>> dataRoots;
std::vector<std::shared_ptr<ASTNode>> topologicalOrder = ApplyKahnAlgorithm(nodeMap);
for (const auto& node : m_nodes) {
if (hasIncomingExec.find(node->GetId()) == hasIncomingExec.end()) {
if (dynamic_cast<FunctionEntryNode*>(node.get())) {
execRoots.push_back(node);
}
}
// Nodes that have data outputs but no data inputs are data path roots
if ((hasIncomingData.find(node->GetId()) == hasIncomingData.end()) ||
AreAllInputsVariables(node, nodeMap))
// Maintenant, on va ajouter les connexions de données
for (const auto& conn : m_connections)
{
auto outNode = nodeMap[conn->outNodeId];
auto inNode = nodeMap[conn->inNodeId];
// Keep variables nodes as data inputs
if (dynamic_cast<VariableNode*>(outNode->node.get()))
{
// Check if the node has any outgoing data connections
bool hasDataOutput = false;
for (const auto& conn : m_connections)
{
if (conn->type == Connection::DATA_LINK &&
conn->outNodeId == node->GetId()) {
hasDataOutput = true;
break;
}
}
if (hasDataOutput && !(dynamic_cast<VariableNode*>(node.get())))
{
dataRoots.push_back(node);
}
// if (hasDataOutput) {
// dataRoots.push_back(node);
// }
inNode->AddDataInput(conn->inPortIndex, outNode);
}
}
std::vector<PathTree> pathTrees;
// Build execution paths
BuildExecutionPath(topologicalOrder, nodeMap);
// Build execution path trees
for (const auto& root : execRoots) {
PathTree tree;
tree.root = std::make_shared<ASTNode>(root);
tree.isExecutionPath = true;
BuildExecutionPath(tree, nodeMap);
pathTrees.push_back(tree);
}
return topologicalOrder;
// Build data path trees
for (const auto& root : dataRoots) {
PathTree tree;
tree.root = std::make_shared<ASTNode>(root);
tree.isExecutionPath = false;
BuildDataPath(tree, nodeMap);
pathTrees.push_back(tree);
}
return pathTrees;
}
private:
const std::vector<std::shared_ptr<BaseNode>>& m_nodes;
const std::vector<std::shared_ptr<Connection>>& m_connections;
void BuildExecutionPath(std::vector<std::shared_ptr<ASTNode>>& tree,
const std::unordered_map<std::string, std::shared_ptr<ASTNode>>& nodeMap)
{
std::queue<std::shared_ptr<ASTNode>> queue;
queue.push(tree.front());
while (!queue.empty()) {
auto current = queue.front();
queue.pop();
// Find execution connections from this node
for (const auto& conn : m_connections)
{
if (conn->outNodeId == current->node->GetId())
{
auto targetNode = nodeMap.find(conn->inNodeId);
if (targetNode != nodeMap.end())
{
auto childNode = targetNode->second;
current->children.push_back(childNode);
queue.push(childNode);
}
}
}
}
}
std::vector<std::shared_ptr<ASTNode>> ApplyKahnAlgorithm(const std::unordered_map<std::string, std::shared_ptr<ASTNode>> &nodeMap)
{
// Pour le container de la queue, on utilise un comparateur pour trier les noeuds par poids
// Cela permet de prioriser les noeuds avec un poids plus faible
auto compare = [](const std::shared_ptr<ASTNode>& a, const std::shared_ptr<ASTNode>& b) {
return a->GetWeight() < b->GetWeight();
};
std::priority_queue<std::shared_ptr<ASTNode>, std::vector<std::shared_ptr<ASTNode>>, decltype(compare)> queue(compare);
// std::queue<std::string> q;
std::unordered_map<std::string, int> inDegree;
std::vector<std::shared_ptr<ASTNode>> res;
int visitedCount = 0;
// Calculate indegree
for (auto p: m_adjList)
{
std::string u = p.first;
// On initialise à zéro si le node n'est pas dans la liste
if (inDegree.find(u) == inDegree.end())
{
inDegree[u] = 0;
}
for (auto v: p.second)
{
inDegree[v]++;
}
}
// insert vertices with 0 indegree in queue
for (auto i: inDegree)
{
if (i.second == 0)
{
queue.push(nodeMap.at(i.first));
}
}
// Process the queue
while(!queue.empty())
{
auto x = queue.top();
queue.pop();
visitedCount++;
res.push_back(x);
// Reduce indegree of neighbours
for (auto dest: m_adjList[x->GetId()])
{
inDegree[dest]--;
if (inDegree[dest] == 0)
{
queue.push(nodeMap.at(dest));
}
}
}
if (visitedCount != nodeMap.size()) {
// cout << "There exists a cycle in the graph";
// throw std::runtime_error("Graph has a cycle");
}
// Debug: print in the console all the nodes in topological order
std::cout << "Topological order: \n\n";
for (const auto& a : res)
{
std::cout << a->node->GetTypeName() << " (" << a->GetId() << ") \n";
}
return res;
}
// Ids (UUID strings) of nodes
std::unordered_map<std::string, std::vector<std::string>> m_adjList;
bool AreAllInputsVariables(const std::shared_ptr<BaseNode>& node, const std::unordered_map<std::string, std::shared_ptr<BaseNode>>& nodeMap) const
{
@ -246,97 +321,9 @@ private:
}
}
}
return true;
return true;
}
void BuildExecutionPath(PathTree& tree,
const std::unordered_map<std::string,
std::shared_ptr<BaseNode>>& nodeMap) {
std::queue<std::shared_ptr<ASTNode>> queue;
queue.push(tree.root);
while (!queue.empty()) {
auto current = queue.front();
queue.pop();
// Find execution connections from this node
for (const auto& conn : m_connections) {
if (conn->type == Connection::EXECUTION_LINKJ &&
conn->outNodeId == current->node->GetId()) {
auto targetNode = nodeMap.find(conn->inNodeId);
if (targetNode != nodeMap.end()) {
auto childNode = std::make_shared<ASTNode>(targetNode->second);
current->children.push_back(childNode);
queue.push(childNode);
tree.connections.push_back(conn);
// For each execution node, find its data inputs
BuildDataInputs(childNode, nodeMap);
}
}
}
}
}
void BuildDataPath(PathTree& tree,
const std::unordered_map<std::string,
std::shared_ptr<BaseNode>>& nodeMap) {
std::queue<std::shared_ptr<ASTNode>> queue;
queue.push(tree.root);
std::unordered_set<std::string> visited;
std::shared_ptr<ASTNode> currentLastNode = nullptr;
while (!queue.empty()) {
auto current = queue.front();
queue.pop();
if (visited.find(current->node->GetId()) != visited.end()) {
continue;
}
currentLastNode = current; // Met à jour le dernier nœud visité
visited.insert(current->node->GetId());
// Find data connections from this node
for (const auto& conn : m_connections) {
if (conn->type == Connection::DATA_LINK &&
conn->outNodeId == current->node->GetId()) {
auto targetNode = nodeMap.find(conn->inNodeId);
if (targetNode != nodeMap.end()) {
auto childNode = std::make_shared<ASTNode>(targetNode->second);
// Add childNode as a children only if it is not an execution node
// Otherwise, the assembly code will be generated twice
if (childNode->IsDataNode())
{
current->dataOutputs[conn->outPortIndex].push_back({childNode, conn->inPortIndex});
}
queue.push(childNode);
tree.connections.push_back(conn);
}
}
}
}
// On garde la trace du dernier nœud visité
// De cette façon on va pouvoir savoir à quel nœud on doit se connecter
// pour la génération de code
tree.lastNode = currentLastNode;
}
void BuildDataInputs(std::shared_ptr<ASTNode> node,
const std::unordered_map<std::string,
std::shared_ptr<BaseNode>>& nodeMap) {
for (const auto& conn : m_connections) {
if (conn->type == Connection::DATA_LINK &&
conn->inNodeId == node->node->GetId()) {
auto sourceNode = nodeMap.find(conn->outNodeId);
if (sourceNode != nodeMap.end()) {
auto inputNode = std::make_shared<ASTNode>(sourceNode->second);
node->dataInputs[conn->inPortIndex] = inputNode;
}
}
}
}
};

View file

@ -188,10 +188,10 @@ public:
}
};
// Generate all constants
for (const auto& astNode : m_ast.nodeMap) {
assemblyCode << astNode.second->node->GenerateConstants();
}
// // Generate all constants
// for (const auto& astNode : m_ast.nodeMap) {
// assemblyCode << astNode.second->node->GenerateConstants();
// }
// After the constants, the main entry point:
assemblyCode << ".main:\n";

View file

@ -62,6 +62,13 @@ protected:
}
virtual void Visit(const std::shared_ptr<Variable> v) {
}
virtual void GenerateVariable(const std::shared_ptr<Variable> v) {
}
virtual void GenerateExit() {
@ -84,7 +91,7 @@ protected:
m_depth++;
// Get condition value
auto conditionNode = node->GetDataInput(0);
int conditionValue = EvaluateCondition(conditionNode);
int conditionValue = 0;
FlowVisualizer::PrintBranchDecision(conditionValue > 7,
std::to_string(conditionValue), m_depth);
@ -105,18 +112,16 @@ protected:
m_depth++;
auto* opNode = node->GetAs<OperatorNode>();
// Evaluate operands
int value1 = EvaluateOperand(node->GetDataInput(0));
int value2 = EvaluateOperand(node->GetDataInput(1));
FlowVisualizer::PrintDataFlow("Operand 1", "ADD",
std::to_string(value1), m_depth);
FlowVisualizer::PrintDataFlow("Operand 2", "ADD",
std::to_string(value2), m_depth);
FlowVisualizer::PrintDataFlow("Operand 1", opNode->GetOperatorSymbol(),
std::to_string(0), m_depth);
FlowVisualizer::PrintDataFlow("Operand 2", opNode->GetOperatorSymbol(),
std::to_string(0), m_depth);
int result = value1 + value2;
FlowVisualizer::PrintDataFlow("ADD", "Result",
std::to_string(result), m_depth);
FlowVisualizer::PrintDataFlow(opNode->GetOperatorSymbol(), "Result",
std::to_string(0), m_depth);
for (const auto& [port, outputs] : node->dataOutputs) {
for (const auto& target : outputs) {
@ -148,24 +153,4 @@ protected:
private:
int m_depth = 0;
int EvaluateOperand(std::shared_ptr<ASTNode> node) {
if (!node) return 0;
if (node->IsType<VariableNode>()) {
auto* varNode = node->GetAs<VariableNode>();
return varNode->GetValue<int>();
}
return 0;
}
int EvaluateCondition(std::shared_ptr<ASTNode> node) {
if (!node) return 0;
if (node->IsType<OperatorNode>()) {
auto input0 = node->GetDataInput(0);
auto input1 = node->GetDataInput(1);
return EvaluateOperand(input0) + EvaluateOperand(input1);
}
return 0;
}
};

View file

@ -2,7 +2,6 @@
#include "story_project.h"
#include "connection.h"
#include "sys_lib.h"
#include "compiler.h"
static std::string ChoiceLabel(const std::string &id)
{

View file

@ -12,8 +12,7 @@ public:
MediaNode(const std::string &type);
virtual void Initialize() override;
virtual std::string Build(IStoryPage &page, const StoryOptions &options, int nb_out_conns) override;
virtual std::string GenerateConstants(IStoryPage &page, IStoryProject &project, int nb_out_conns) override;
void SetImage(const std::string &image);
std::string_view GetImage() const;

View file

@ -19,44 +19,6 @@ BaseNode::~BaseNode()
std::cout << "Deleted base node" << std::endl;
}
std::string BaseNode::GenerateRandomString(size_t length, uint32_t flags)
{
std::string charset = "";
if (flags & CHARSET_ALPHABET_LOWER)
{
charset += "abcdefghijklmnopqrstuvwxyz";
}
if (flags & CHARSET_ALPHABET_UPPER)
{
charset += "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
}
if (flags & CHARSET_NUMBERS)
{
charset += "0123456789";
}
if (flags & CHARSET_SIGNS)
{
charset += "!@#$%^&*()_+-=[]{}|;:,.<>?";
}
std::random_device rd;
std::mt19937 generator(rd());
std::uniform_int_distribution<> distribution(0, charset.size() - 1);
std::string result;
result.reserve(length);
for (size_t i = 0; i < length; ++i) {
result += charset[distribution(generator)];
}
return result;
}
std::string BaseNode::GetEntryLabel(const std::string &id)
{
@ -128,3 +90,13 @@ float BaseNode::GetY() const
{
return m_pos.y;
}
void BaseNode::Accept(IVariableVisitor &visitor)
{
for (auto &v : m_variables)
{
visitor.Visit(v.second);
}
}

View file

@ -9,6 +9,14 @@
#include "i_story_page.h"
#include "i_story_project.h"
#include "story_options.h"
#include "variable.h"
class IVariableVisitor {
public:
virtual void Visit(const std::shared_ptr<Variable> v) = 0;
};
class BaseNode
{
@ -36,14 +44,6 @@ public:
float y;
};
enum RandomFlags
{
CHARSET_ALPHABET_LOWER = 0x1, // "abcdefghijklmnopqrstuvwxyz"
CHARSET_ALPHABET_UPPER = 0x2, // "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
CHARSET_NUMBERS = 0x4, // "0123456789"
CHARSET_SIGNS = 0x8, // "!@#$%^&*()_+-=[]{}|;:,.<>?";
ALL_CHARSETS = CHARSET_ALPHABET_LOWER | CHARSET_ALPHABET_UPPER |CHARSET_NUMBERS | CHARSET_SIGNS
};
struct ConstantValue
{
@ -69,11 +69,6 @@ public:
static std::string GetEntryLabel(const std::string &id);
virtual void Initialize() = 0;
virtual std::string Build(IStoryPage &page, const StoryOptions &options, int nb_out_conns) = 0;
virtual std::string GenerateConstants(IStoryPage &page, IStoryProject &project, int nb_out_conns) = 0;
virtual std::string GenerateConstants() const { return ""; }
virtual std::string GenerateAssembly() const = 0;
void SetPosition(float x, float y);
@ -97,6 +92,15 @@ public:
return m_typeName;
}
void SetWeight(int w) {
m_weight = w;
}
int GetWeight() const {
return m_weight;
}
void SetId(const std::string &id) { m_uuid = id; }
std::string GetId() const { return m_uuid; }
@ -110,8 +114,6 @@ public:
void SetInternalData(const nlohmann::json &j);
nlohmann::json GetInternalData() const;
static std::string GenerateRandomString(size_t length, uint32_t flags = RandomFlags::ALL_CHARSETS);
void ClearPorts() {
m_inputPorts.clear();
m_outputPorts.clear();
@ -134,12 +136,21 @@ public:
return m_behavior;
}
void Accept(IVariableVisitor &visitor);
protected:
// Easy access the variables for children nodes
// Key is the variable name, or whatever the node use to identify the variable
std::map<std::string, std::shared_ptr<Variable>> m_variables;
private:
std::string m_title{"Default title"};
std::string m_type;
std::string m_typeName;
std::string m_uuid;
NodePosition m_pos;
int m_weight{0};
Behavior m_behavior{BEHAVIOR_EXECUTION};
std::vector<Port> m_inputPorts;

View file

@ -2,7 +2,6 @@
#include "story_project.h"
#include "connection.h"
#include "sys_lib.h"
#include "compiler.h"
BranchNode::BranchNode(const std::string &type)
@ -17,37 +16,3 @@ void BranchNode::Initialize()
}
std::string BranchNode::GenerateConstants(IStoryPage &page, IStoryProject &project, int nb_out_conns)
{
std::string s;
return s;
}
std::string BranchNode::Build(IStoryPage &page, const StoryOptions &options, int nb_out_conns)
{
std::stringstream ss;
std::list<std::shared_ptr<Connection>> conns;
page.GetNodeConnections(conns, GetId());
int i = 0;
std::list<std::shared_ptr<Connection>>::iterator c = conns.begin();
if (conns.size() == 2)
{
ss << R"(; ---------------------------- )"
<< GetTitle()
<< " Type: Branch"
<< "\n";
ss << "eq r0, r0, r1\n"
<< "skipz r0\n"
<< "jump " << BaseNode::GetEntryLabel((*c)->inNodeId);
++c;
ss << "jump " << BaseNode::GetEntryLabel((*c)->inNodeId);
}
return ss.str();
}

View file

@ -12,10 +12,6 @@ public:
BranchNode(const std::string &type);
virtual void Initialize() override;
virtual std::string Build(IStoryPage &page, const StoryOptions &options, int nb_out_conns) override;
virtual std::string GenerateConstants(IStoryPage &page, IStoryProject &project, int nb_out_conns) override;
virtual std::string GenerateAssembly() const { return ""; }
private:
};

View file

@ -2,7 +2,6 @@
#include "story_project.h"
#include "connection.h"
#include "sys_lib.h"
#include "compiler.h"
CompareNode::CompareNode(const std::string &type)
@ -17,63 +16,3 @@ void CompareNode::Initialize()
}
std::string CompareNode::GenerateConstants(IStoryPage &page, IStoryProject &project, int nb_out_conns)
{
std::string s;
return s;
}
std::string CompareNode::Build(IStoryPage &page, const StoryOptions &options, int nb_out_conns)
{
std::stringstream ss;
std::list<std::shared_ptr<Connection>> conns;
page.GetNodeConnections(conns, GetId());
int i = 0;
std::list<std::shared_ptr<Connection>>::iterator c = conns.begin();
/*
; Déclaration des variables en RAM
$var1 DV32 1 ; Première variable à comparer
$var2 DV32 1 ; Deuxième variable à comparer
; Code principal
.compare_ge:
; Charger les valeurs des variables dans les registres
lcons r1, $var1
load r1, @r1, 4 ; Charger 4 bytes (32 bits) de var1 dans r1
lcons r2, $var2
load r2, @r2, 4 ; Charger 4 bytes (32 bits) de var2 dans r2
; Comparer r1 >= r2
gt r3, r1, r2 ; r3 = 1 si r1 > r2, sinon 0
eq r4, r1, r2 ; r4 = 1 si r1 == r2, sinon 0
or r0, r3, r4 ; r0 = 1 si r1 > r2 OU r1 == r2, sinon 0
ret
*/
if (conns.size() == 2)
{
ss << R"(; ---------------------------- )"
<< GetTitle()
<< " Type: Branch"
<< "\n";
ss << "eq r0, r0, r1\n"
<< "skipz r0\n"
<< "jump " << BaseNode::GetEntryLabel((*c)->inNodeId);
++c;
ss << "jump " << BaseNode::GetEntryLabel((*c)->inNodeId);
}
return ss.str();
}

View file

@ -12,8 +12,6 @@ public:
CompareNode(const std::string &type);
virtual void Initialize() override;
virtual std::string Build(IStoryPage &page, const StoryOptions &options, int nb_out_conns) override;
virtual std::string GenerateConstants(IStoryPage &page, IStoryProject &project, int nb_out_conns) override;
private:

View file

@ -1,36 +0,0 @@
#pragma once
#include "base_node.h"
class ExecutionNode : public BaseNode
{
public:
ExecutionNode(const std::string &type, const std::string &typeName)
: BaseNode(type, typeName) {}
void Initialize() override {
// Initialisation spécifique pour ExecutionNode
}
std::string Build(IStoryPage &page, const StoryOptions &options, int nb_out_conns) override {
return GetMyEntryLabel() + ":\n";
}
std::string GenerateAssembly() const override {
return GetMyEntryLabel() + ":\n";
}
std::string GenerateConstants(IStoryPage &page, IStoryProject &project, int nb_out_conns) override {
// Génération des constantes pour ExecutionNode
return "ExecutionNode Constants";
}
// Ajoutez des méthodes spécifiques pour gérer les entrées et sorties d'exécution
void AddExecutionInput() {
// Logique pour ajouter une entrée d'exécution
}
void AddExecutionOutput() {
// Logique pour ajouter une sortie d'exécution
}
};

View file

@ -1,13 +1,16 @@
#pragma once
#include "execution_node.h"
#include "base_node.h"
class FunctionEntryNode : public ExecutionNode
class FunctionEntryNode : public BaseNode
{
public:
FunctionEntryNode(const std::string &type)
: ExecutionNode(type, "Function Entry Node") {}
: BaseNode(type, "Function Entry Node")
{
SetWeight(100);
}
void Initialize() override {
// Initialisation spécifique pour FunctionEntryNode
@ -19,11 +22,6 @@ public:
return GetMyEntryLabel() + ":\n";
}
std::string GenerateConstants(IStoryPage &page, IStoryProject &project, int nb_out_conns) override {
// Génération des constantes pour FunctionEntryNode
return "FunctionEntryNode Constants";
}
// Ajoutez des méthodes spécifiques pour gérer l'entrée de la fonction
void PrepareFunctionEntry() {
// Logique pour préparer l'entrée de la fonction

View file

@ -18,11 +18,6 @@ public:
return "FunctionExitNode Build";
}
std::string GenerateConstants(IStoryPage &page, IStoryProject &project, int nb_out_conns) override {
// Génération des constantes pour FunctionExitNode
return "FunctionExitNode Constants";
}
// Ajoutez des méthodes spécifiques pour gérer la sortie de la fonction
void FinalizeFunctionExit() {
// Logique pour finaliser la sortie de la fonction

View file

@ -27,17 +27,3 @@ void FunctionNode::Initialize()
// m_sound = j["sound"].get<std::string>();
}
std::string FunctionNode::Build(IStoryPage &page, const StoryOptions &options, int nb_out_conns)
{
return std::string();
}
std::string FunctionNode::GenerateConstants(IStoryPage &page, IStoryProject &project, int nb_out_conns)
{
std::string s;
return s;
}

View file

@ -12,8 +12,6 @@ public:
FunctionNode(const std::string &type);
virtual void Initialize() override;
virtual std::string Build(IStoryPage &page, const StoryOptions &options, int nb_out_conns) override;
virtual std::string GenerateConstants(IStoryPage &page, IStoryProject &project, int nb_out_conns) override;
void StoreInternalData();

View file

@ -105,56 +105,6 @@ public:
return ss.str();
}
std::string GenerateConstants(IStoryPage& page, IStoryProject& project, int nb_out_conns) override {
return ""; // Operators don't generate constants
}
std::string GenerateAssembly() const override {
std::stringstream ss;
switch (m_operationType) {
case OperationType::ADD:
ss << " pop ebx\n"
<< " pop eax\n"
<< " add eax, ebx\n"
<< " push eax\n";
break;
case OperationType::SUBTRACT:
ss << " pop ebx\n"
<< " pop eax\n"
<< " sub eax, ebx\n"
<< " push eax\n";
break;
case OperationType::MULTIPLY:
ss << " pop ebx\n"
<< " pop eax\n"
<< " imul eax, ebx\n"
<< " push eax\n";
break;
case OperationType::DIVIDE:
ss << " pop ebx\n"
<< " pop eax\n"
<< " cdq\n" // Sign extend eax into edx
<< " idiv ebx\n"
<< " push eax\n"; // Push quotient
break;
case OperationType::AND:
ss << " pop ebx\n"
<< " pop eax\n"
<< " and eax, ebx\n"
<< " push eax\n";
break;
case OperationType::OR:
ss << " pop ebx\n"
<< " pop eax\n"
<< " or eax, ebx\n"
<< " push eax\n";
break;
// Add other operators...
}
return ss.str();
}
private:
OperationType m_operationType;

View file

@ -2,13 +2,16 @@
#include "story_project.h"
#include "connection.h"
#include "sys_lib.h"
#include "compiler.h"
#include "variable.h"
PrintNode::PrintNode(const std::string &type)
: ExecutionNode(type, "Print Node")
: BaseNode(type, "Print Node")
{
m_label = GenerateRandomString(10, BaseNode::CHARSET_ALPHABET_LOWER | BaseNode::CHARSET_ALPHABET_UPPER );// Should be enough to avoid collision?
// Create empty variable in memory
auto v = std::make_shared<Variable>(m_label);
v->SetTextValue("");
m_label = v->GetLabel();
m_variables[m_label] = v;
}
@ -17,41 +20,9 @@ void PrintNode::Initialize()
}
std::string PrintNode::GenerateConstants(IStoryPage &page, IStoryProject &project, int nb_out_conns)
{
std::stringstream ss;
ss << "$" << m_label << " DC8, " << m_text << "\n";
return ss.str();
}
std::string PrintNode::Build(IStoryPage &page, const StoryOptions &options, int nb_out_conns)
{
return "";
}
std::string PrintNode::GenerateConstants() const
{
std::stringstream ss;
ss << "$" << m_label << " DC8, \"" << m_text << "\"\n";
return ss.str();
}
std::string PrintNode::GenerateAssembly() const
{
std::stringstream ss;
ss << ExecutionNode::GenerateAssembly()
<< " push r0\n"
<< " push r1\n"
<< " lcons r0, $" << m_label << "\n"
<< " lcons r1, 0 ; number of arguments\n" // FIXME: handle arguments
<< " syscall 4\n"
<< " pop r1\n"
<< " pop r0\n";
// << ""mov r2, %2 // arguments are in r2, r3, r4 etc.
return ss.str();
}

View file

@ -2,34 +2,33 @@
#include <string>
#include "i_story_manager.h"
#include "execution_node.h"
#include "base_node.h"
#include "i_script_node.h"
#include "i_story_project.h"
class PrintNode : public ExecutionNode
class PrintNode : public BaseNode
{
public:
PrintNode(const std::string &type);
virtual void Initialize() override;
virtual std::string Build(IStoryPage &page, const StoryOptions &options, int nb_out_conns) override;
virtual std::string GenerateConstants(IStoryPage &page, IStoryProject &project, int nb_out_conns) override;
virtual std::string GenerateConstants() const override;
std::string GenerateAssembly() const override;
void SetText(const std::string &text) {
m_text = text;
m_variables.at(m_label)->SetValue<std::string>(text);
}
std::string GetLabel() const {
return m_label;
}
std::string GetText() const {
return m_text;
return m_variables.at(m_label)->GetValue<std::string>();
}
private:
std::string m_label;
std::string m_text; // Text to print
std::string m_label; // Label for the string literal
uint32_t m_arguments{0}; // number of arguments
};

View file

@ -27,17 +27,5 @@ void VariableNode::Initialize()
// m_sound = j["sound"].get<std::string>();
}
std::string VariableNode::Build(IStoryPage &page, const StoryOptions &options, int nb_out_conns)
{
return std::string();
}
std::string VariableNode::GenerateConstants(IStoryPage &page, IStoryProject &project, int nb_out_conns)
{
std::string s;
return s;
}

View file

@ -1,8 +1,8 @@
#pragma once
#include <string>
#include <variant>
#include "variable.h"
#include "i_story_manager.h"
#include "base_node.h"
#include "i_script_node.h"
@ -12,92 +12,22 @@ class VariableNode : public BaseNode
{
public:
enum class ValueType {
INTEGER,
FLOAT,
BOOL,
STRING
};
using VariableValue = std::variant<int, float, bool, std::string>;
VariableNode(const std::string &type = "variable-node");
virtual void Initialize() override;
virtual std::string Build(IStoryPage &page, const StoryOptions &options, int nb_out_conns) override;
virtual std::string GenerateConstants(IStoryPage &page, IStoryProject &project, int nb_out_conns) override;
virtual std::string GenerateAssembly() const { return ""; }
void StoreInternalData();
// Setters
void SetVariableName(const std::string& name) {
m_variableName = name;
void SetVariable(const std::shared_ptr<Variable> v) {
m_variable = v;
}
void SetConstant(bool isConstant) {
m_isConstant = isConstant;
std::shared_ptr<Variable> GetVariable() const {
return m_variable;
}
void SetValueType(ValueType type) {
m_valueType = type;
// Reset value to default for new type
switch (type) {
case ValueType::INTEGER:
m_value = 0;
break;
case ValueType::FLOAT:
m_value = 0.0f;
break;
case ValueType::BOOL:
m_value = false;
break;
case ValueType::STRING:
m_value = "";
break;
}
}
template<typename T>
void SetValue(const T& value) {
try {
m_value = value;
} catch (const std::bad_variant_access&) {
throw std::runtime_error("Invalid value type for variable");
}
}
// Getters
std::string GetVariableName() const {
return m_variableName;
}
bool IsConstant() const {
return m_isConstant;
}
ValueType GetValueType() const {
return m_valueType;
}
template<typename T>
T GetValue() const {
try {
return std::get<T>(m_value);
} catch (const std::bad_variant_access&) {
throw std::runtime_error("Invalid value type requested");
}
}
private:
std::string m_variableName;
ValueType m_valueType;
VariableValue m_value;
bool m_isConstant;
std::shared_ptr<Variable> m_variable;
};

View file

@ -52,16 +52,16 @@ public:
void Build(std::stringstream &code, IStoryProject &project)
{
// First generate all constants
for (const auto & n : m_nodes)
{
code << n->GenerateConstants(*this, project, OutputsCount(n->GetId())) << "\n";
}
// // First generate all constants
// for (const auto & n : m_nodes)
// {
// code << n->GenerateConstants(*this, project, OutputsCount(n->GetId())) << "\n";
// }
for (const auto & n : m_nodes)
{
code << n->Build(*this, project.GetOptions(), OutputsCount(n->GetId())) << "\n";
}
// for (const auto & n : m_nodes)
// {
// code << n->Build(*this, project.GetOptions(), OutputsCount(n->GetId())) << "\n";
// }
}
virtual void GetNodeConnections(std::list<std::shared_ptr<Connection>> &c, const std::string &nodeId) override

View file

@ -7,7 +7,7 @@
#include "story_project.h"
#include "json.hpp"
#include "media_node.h"
// #include "media_node.h"
#include "function_node.h"
#include "variable_node.h"
#include "sys_lib.h"
@ -15,7 +15,7 @@
StoryProject::StoryProject(ILogger &log)
: m_log(log)
{
registerNode<MediaNode>("media-node");
// registerNode<MediaNode>("media-node");
registerNode<FunctionNode>("function-node");
registerNode<VariableNode>("variable-node");
}
@ -223,7 +223,7 @@ std::pair<std::list<std::shared_ptr<Connection>>::iterator, std::list<std::share
return std::pair<std::list<std::shared_ptr<Connection>>::iterator, std::list<std::shared_ptr<Connection>>::iterator>();
}
void StoryProject::ScanVariable(const std::function<void(Variable& element)>& operation)
void StoryProject::ScanVariable(const std::function<void(std::shared_ptr<Variable> element)>& operation)
{
for (auto &v : m_variables)
{
@ -233,7 +233,13 @@ void StoryProject::ScanVariable(const std::function<void(Variable& element)>& op
void StoryProject::AddVariable()
{
m_variables.push_back(Variable("var_" + std::to_string(m_variables.size()), "int32_t", 0, 8));
auto v = std::make_shared<Variable>("var_" + std::to_string(m_variables.size()));
v->SetValue(0);
v->SetValueType(Variable::ValueType::INTEGER);
v->SetConstant(false);
m_variables.push_back(v);
}
void StoryProject::DeleteVariable(int i)

View file

@ -103,7 +103,7 @@ public:
std::pair<std::list<std::shared_ptr<Connection>>::iterator, std::list<std::shared_ptr<Connection>>::iterator> Links(const std::string_view &page_uuid);
void ScanVariable(const std::function<void(Variable& element)>& operation);
void ScanVariable(const std::function<void(std::shared_ptr<Variable> element)>& operation);
void AddVariable();
void DeleteVariable(int i);
@ -134,7 +134,7 @@ private:
std::list<std::shared_ptr<StoryPage>> m_pages;
std::vector<Variable> m_variables;
std::vector<std::shared_ptr<Variable>> m_variables;
StoryOptions m_storyOptions;

View file

@ -35,6 +35,7 @@ THE SOFTWARE.
#include "function_entry_node.h"
#include "operator_node.h"
#include "chip32_machine.h"
#include "variable.h"
#include <stdarg.h>
#include <string.h>
@ -43,47 +44,7 @@ THE SOFTWARE.
#include "assembly_generator.h"
#include "flow_generator.h"
#include "assembly_generator_chip32.h"
/*
void ProcessASTTree(const ASTBuilder::PathTree& tree, int depth = 0) {
std::queue<std::pair<std::shared_ptr<ASTNode>, int>> queue;
queue.push({tree.root, depth});
std::unordered_set<std::string> visited;
while (!queue.empty()) {
auto [node, currentDepth] = queue.front();
queue.pop();
if (visited.find(node->node->GetId()) != visited.end()) {
continue;
}
visited.insert(node->node->GetId());
std::string indent(currentDepth * 2, ' ');
std::cout << indent << "Node: " << node->node->GetTypeName()
<< " (ID: " << node->node->GetId() << ")" << std::endl;
// Print data inputs
for (const auto& [portIndex, inputNode] : node->dataInputs) {
std::cout << indent << " Input at port " << portIndex
<< " from: " << inputNode->node->GetTypeName() << std::endl;
}
// Print data outputs
for (const auto& [portIndex, outputs] : node->dataOutputs) {
for (const auto& [targetNode, targetPort] : outputs) {
std::cout << indent << " Output from port " << portIndex
<< " to: " << targetNode->node->GetTypeName()
<< " port " << targetPort << std::endl;
}
}
// Add children to queue
for (const auto& child : node->children) {
queue.push({child, currentDepth + 1});
}
}
}
*/
TEST_CASE( "Check various indentations and typos" ) {
Compiler compiler;
@ -97,21 +58,51 @@ TEST_CASE( "Check various indentations and typos" ) {
auto branchNode = std::make_shared<BranchNode>("branch-node");
auto functionEntryNode = std::make_shared<FunctionEntryNode>("function-entry-node");
auto variableNode1 = std::make_shared<VariableNode>("variable-node");
variableNode1->SetValue(5);
variableNode1->SetValueType(VariableNode::ValueType::INTEGER);
variableNode1->SetVariableName("X");
std::vector<std::shared_ptr<Variable>> variables;
auto var1 = std::make_shared<Variable>("X");
var1->SetValue(5);
var1->SetValueType(Variable::ValueType::INTEGER);
auto variableNode2 = std::make_shared<VariableNode>("variable-node");
variableNode2->SetValue(10);
variableNode2->SetValueType(VariableNode::ValueType::INTEGER);
variableNode2->SetVariableName("Y");
auto var2 = std::make_shared<Variable>("Y");
var2->SetValue(10);
var2->SetValueType(Variable::ValueType::INTEGER);
auto var3 = std::make_shared<Variable>("A");
var3->SetValue(7);
var3->SetValueType(Variable::ValueType::INTEGER);
auto var4 = std::make_shared<Variable>("B");
var4->SetValue(2);
var4->SetValueType(Variable::ValueType::INTEGER);
variables.push_back(var1);
variables.push_back(var2);
variables.push_back(var3);
variables.push_back(var4);
auto variableNodeX = std::make_shared<VariableNode>("variable-node");
variableNodeX->SetVariable(var1);
auto variableNodeY = std::make_shared<VariableNode>("variable-node");
variableNodeY->SetVariable(var2);
auto variableNodeA = std::make_shared<VariableNode>("variable-node");
variableNodeA->SetVariable(var3);
auto variableNodeB = std::make_shared<VariableNode>("variable-node");
variableNodeB->SetVariable(var4);
auto testNode = std::make_shared<OperatorNode>();
testNode->SetOperationType(OperatorNode::OperationType::GREATER_THAN);
auto addNode = std::make_shared<OperatorNode>();
addNode->SetOperationType(OperatorNode::OperationType::ADD);
auto subNode = std::make_shared<OperatorNode>();
subNode->SetOperationType(OperatorNode::OperationType::SUBTRACT);
std::vector<std::shared_ptr<BaseNode>> nodes;
@ -119,9 +110,13 @@ TEST_CASE( "Check various indentations and typos" ) {
nodes.push_back(printNodeTrue);
nodes.push_back(printNodeFalse);
nodes.push_back(branchNode);
nodes.push_back(variableNode1);
nodes.push_back(variableNode2);
nodes.push_back(variableNodeX);
nodes.push_back(variableNodeY);
nodes.push_back(variableNodeA);
nodes.push_back(variableNodeB);
nodes.push_back(testNode);
nodes.push_back(addNode);
nodes.push_back(subNode);
auto cn1 = std::make_shared<Connection>();
auto cn2 = std::make_shared<Connection>();
@ -129,6 +124,10 @@ TEST_CASE( "Check various indentations and typos" ) {
auto cn4 = std::make_shared<Connection>();
auto cn5 = std::make_shared<Connection>();
auto cn6 = std::make_shared<Connection>();
auto cn7 = std::make_shared<Connection>();
auto cn8 = std::make_shared<Connection>();
auto cn9 = std::make_shared<Connection>();
auto cn10 = std::make_shared<Connection>();
std::vector<std::shared_ptr<Connection>> connections;
@ -138,7 +137,10 @@ TEST_CASE( "Check various indentations and typos" ) {
connections.push_back(cn4);
connections.push_back(cn5);
connections.push_back(cn6);
connections.push_back(cn7);
connections.push_back(cn8);
connections.push_back(cn9);
connections.push_back(cn10);
// Branch True -> print Ok
// False -> print Ko
@ -168,47 +170,47 @@ TEST_CASE( "Check various indentations and typos" ) {
cn4->outPortIndex = 0;
cn4->type = Connection::DATA_LINK;
// Variable 1 -> Compare test node input 1
// + output 1 -> Compare test node input 1
cn5->inNodeId = testNode->GetId();
cn5->inPortIndex = 0;
cn5->outNodeId = variableNode1->GetId();
cn5->outNodeId = addNode->GetId();
cn5->outPortIndex = 0;
cn5->type = Connection::DATA_LINK;
// Variable 1 -> Compare test node input 1
// - output -> Compare test node input 1
cn6->inNodeId = testNode->GetId();
cn6->inPortIndex = 1;
cn6->outNodeId = variableNode2->GetId();
cn6->outNodeId = subNode->GetId();
cn6->outPortIndex = 0;
cn6->type = Connection::DATA_LINK;
// ADD NODE INPUTS
// // Création des nœuds
// std::vector<Node> nodes = {
// Node(Node::Type::VARIABLE, "i", "node_i"),
// Node(Node::Type::CONSTANT, 10, "node_10"),
// Node(Node::Type::SUBTRACT, "node_subtract"),
// Node(Node::Type::CONSTANT, 1, "node_1"),
// Node(Node::Type::ADD, "node_add"),
// Node(Node::Type::ASSIGN, "i", "node_assign"),
// Node(Node::Type::VARIABLE, "conditionVar", "node_condVar"),
// Node(Node::Type::CONSTANT, 2, "node_2"),
// Node(Node::Type::MULTIPLY, "node_multiply"),
// Node(Node::Type::ASSIGN, "i", "node_assign_multiply"),
// Node(Node::Type::BRANCH, "node_branch"),
// Node(Node::Type::LOOP, "node_loop")
// };
cn7->inNodeId = addNode->GetId();
cn7->inPortIndex = 0;
cn7->outNodeId = variableNodeX->GetId();
cn7->outPortIndex = 0;
cn7->type = Connection::DATA_LINK;
// try
// {
// // Construction de l'AST
// compiler.buildAST(nodes, connections);
// compiler.printAST();
cn8->inNodeId = addNode->GetId();
cn8->inPortIndex = 1;
cn8->outNodeId = variableNodeY->GetId();
cn8->outPortIndex = 0;
cn8->type = Connection::DATA_LINK;
// } catch(const std::exception &e)
// {
// std::cout << e.what() << std::endl;
// }
// SUBTRACT NODE INPUTS
cn9->inNodeId = subNode->GetId();
cn9->inPortIndex = 0;
cn9->outNodeId = variableNodeA->GetId();
cn9->outPortIndex = 0;
cn9->type = Connection::DATA_LINK;
cn10->inNodeId = subNode->GetId();
cn10->inPortIndex = 1;
cn10->outNodeId = variableNodeB->GetId();
cn10->outPortIndex = 0;
cn10->type = Connection::DATA_LINK;
// Create generator context with current time and user
@ -225,40 +227,24 @@ TEST_CASE( "Check various indentations and typos" ) {
ASTBuilder builder(nodes, connections);
auto pathTrees = builder.BuildAST();
/*
// Process each path tree
for (const auto& tree : pathTrees) {
std::cout << (tree.isExecutionPath ? "Execution" : "Data")
<< " Path Tree:" << std::endl;
ProcessASTTree(tree);
std::cout << std::endl;
}
*/
auto pathTree = builder.BuildAST();
// Generate flow in the console
VisualFlowGenerator flowGenerator(context);
FlowVisualizer::PrintHeader("arabine", "2025-04-08 12:03:01");
std::string flow = flowGenerator.GenerateAssembly(nodes, pathTrees);
std::string flow = flowGenerator.GenerateAssembly(nodes, pathTree, variables);
std::cout << "\nGenerated flow:\n" << flow << std::endl;
// Generate assembly
std::string assembly = generator.GenerateAssembly(nodes, pathTrees);
std::string assembly = generator.GenerateAssembly(nodes, pathTree, variables);
std::cout << "\nGenerated assembly:\n" << assembly << std::endl;
// compiler.generateAssembly();
Chip32::Machine machine;
// std::cout << compiler.GetCode() << std::endl;
machine.QuickExecute(assembly);
// Chip32::Machine machine;
// machine.QuickExecute(compiler.GetCode());
// REQUIRE( parseResult == true );
}

View file

@ -130,7 +130,7 @@ set(SRCS
src/windows/cpu_window.cpp
src/windows/variables_window.cpp
src/node_editor/media_node_widget.cpp
# src/node_editor/media_node_widget.cpp
src/node_editor/base_node_widget.cpp
src/node_editor/node_editor_window.cpp
src/node_editor/function_node_widget.cpp
@ -168,16 +168,17 @@ set(SRCS
# Core engine files
../core/story-manager/src/compiler.cpp
../core/story-manager/src/story_project.cpp
../core/story-manager/src/story_page.cpp
../core/story-manager/src/base_node.cpp
../core/story-manager/src/media_node.cpp
../core/story-manager/src/compare_node.cpp
../core/story-manager/src/branch_node.cpp
../core/story-manager/src/variable_node.cpp
../core/story-manager/src/function_node.cpp
../core/story-manager/src/connection.cpp
../core/story-manager/src/nodes/base_node.cpp
../core/story-manager/src/nodes/compare_node.cpp
../core/story-manager/src/nodes/branch_node.cpp
../core/story-manager/src/nodes/variable_node.cpp
../core/story-manager/src/nodes/function_node.cpp
../core/story-manager/src/nodes/connection.cpp
../core/story-manager/lib/sys_lib.cpp
../core/story-manager/lib/resource.cpp
@ -222,6 +223,8 @@ target_include_directories(${STORY_EDITOR_PROJECT} PUBLIC
../core/chip32
../shared
../core/story-manager/src
../core/story-manager/src/nodes
../core/story-manager/src/compiler
../core/story-manager/lib
../core/story-manager/interfaces
)

105
story-editor/imgui.ini Normal file
View file

@ -0,0 +1,105 @@
[Window][WindowOverViewport_11111111]
Pos=60,26
Size=1220,694
Collapsed=0
[Window][Debug##Default]
Pos=60,60
Size=400,400
Collapsed=0
[Window][Library Manager]
Pos=672,26
Size=608,694
Collapsed=0
DockId=0x00000002,0
[Window][Console]
Pos=386,659
Size=1152,344
Collapsed=0
[Window][Emulator]
Pos=269,26
Size=401,694
Collapsed=0
DockId=0x00000005,1
[Window][Code viewer]
Pos=269,26
Size=401,694
Collapsed=0
DockId=0x00000005,0
[Window][Resources]
Pos=672,26
Size=608,694
Collapsed=0
DockId=0x00000002,1
[Window][Node editor]
Pos=96,129
Size=394,407
Collapsed=0
[Window][TOOLBAR]
Pos=96,105
Size=79,42
Collapsed=0
[Window][Variables]
Pos=199,187
Size=121,72
Collapsed=0
[Window][CPU]
Pos=515,59
Size=626,744
Collapsed=0
[Window][RAM view]
Pos=378,79
Size=738,442
Collapsed=0
[Window][Properties]
Pos=754,344
Size=626,744
Collapsed=0
[Window][ToolBar]
Pos=0,26
Size=60,694
Collapsed=0
[Window][QuitConfirm]
Pos=479,312
Size=321,96
Collapsed=0
[Table][0x7728942D,5]
RefScale=20
Column 0 Width=44 Sort=0v
Column 1 Width=72
Column 2 Width=104
Column 3 Width=54
Column 4 Width=75
[Table][0x69D69F59,2]
RefScale=20
Column 0 Width=22 Sort=0v
Column 1 Width=68
[Table][0x30BF8F98,3]
RefScale=20
Column 0 Width=259 Sort=0v
Column 1 Width=88
Column 2 Width=124
[Docking][Data]
DockSpace ID=0x08BD597D Window=0x1BBC0F80 Pos=60,26 Size=1220,694 Split=X
DockNode ID=0x00000001 Parent=0x08BD597D SizeRef=610,694 Split=X
DockNode ID=0x00000004 Parent=0x00000001 SizeRef=397,600 CentralNode=1 Selected=0x63869CAF
DockNode ID=0x00000005 Parent=0x00000001 SizeRef=401,600 Selected=0x4B07C626
DockNode ID=0x00000002 Parent=0x08BD597D SizeRef=608,694 Selected=0x30401527

View file

@ -10,7 +10,7 @@
#include "i_story_project.h"
#include "gui.h"
#include <imgui_node_editor.h>
#include "media_node.h"
class FunctionNodeWidget : public BaseNodeWidget
{

View file

@ -28,7 +28,7 @@ NodeEditorWindow::NodeEditorWindow(IStoryManager &manager)
, m_manager(manager)
{
registerNode<MediaNodeWidget>("media-node");
// registerNode<MediaNodeWidget>("media-node");
registerNode<FunctionNodeWidget>("function-node");
registerNode<VariableNodeWidget>("variable-node");
}

View file

@ -32,15 +32,16 @@ void VariableNodeWidget::DrawProperties()
if (ImGui::BeginCombo("Variables list", m_selectedVariable.c_str(), flags))
{
int i = 0;
m_manager.ScanVariable([&i, this] (Variable &var) {
m_manager.ScanVariable([&i, this] (std::shared_ptr<Variable> var) {
// ImGui::PushID(static_cast<int>(i)); // Assure l'unicité des widgets
const bool is_selected = (m_selectedIndex == i);
if (ImGui::Selectable(var.name.c_str(), is_selected))
std::string l = var->GetVariableName();
if (ImGui::Selectable(l.c_str(), is_selected))
{
m_selectedIndex = i;
m_selectedVariable = var.name;
m_selectedVariable = l;
}
// Set the initial focus when opening the combo (scrolling + keyboard navigation focus)

View file

@ -1096,7 +1096,7 @@ uint32_t MainWindow::GetRegister(int reg)
return regVal;
}
void MainWindow::ScanVariable(const std::function<void(Variable& element)>& operation)
void MainWindow::ScanVariable(const std::function<void(std::shared_ptr<Variable> element)>& operation)
{
if (m_story)
{

View file

@ -153,7 +153,7 @@ private:
virtual uint32_t GetRegister(int reg) override;
// Variable
virtual void ScanVariable(const std::function<void(Variable& element)>& operation) override;
virtual void ScanVariable(const std::function<void(std::shared_ptr<Variable> element)>& operation) override;
virtual void AddVariable() override;
virtual void DeleteVariable(int i);

View file

@ -42,47 +42,52 @@ void VariablesWindow::ShowRAMEditor()
ImGui::Separator();
int i = 0;
m_story.ScanVariable([&i, this] (Variable &var) {
m_story.ScanVariable([&i, this] (std::shared_ptr<Variable> var) {
ImGui::PushID(static_cast<int>(i)); // Assure l'unicité des widgets
if (ImGui::TreeNode((var.name + "###variable").c_str()))
std::string l = var->GetVariableName();
if (ImGui::TreeNode((l + "###variable").c_str()))
{
// Modifier le nom de la variable
static char buffer[Variable::NameMaxSize];
std::strncpy(buffer, var.name.c_str(), sizeof(buffer));
std::strncpy(buffer, l.c_str(), sizeof(buffer));
buffer[sizeof(buffer) - 1] = '\0'; // Assure la terminaison
if (ImGui::InputText("Name", buffer, sizeof(buffer))) {
var.name = buffer;
var->SetVariableName(buffer);
}
// Choisir le type de la variable
const char* types[] = {"Integer", "String"};
static int selectedType = (var.type == "Integer") ? 0 : 1;
static int selectedType = var->IsInteger() ? 0 : 1; // 0 for Integer, 1 for String
if (ImGui::Combo("Type", &selectedType, types, IM_ARRAYSIZE(types))) {
var.type = types[selectedType];
var->SetValueType(selectedType == 0 ? Variable::ValueType::INTEGER : Variable::ValueType::STRING);
}
if (var.type == "Integer")
if (var->IsInteger())
{
// Modifier l'échelle
ImGui::InputInt("Scale Power (10^x)", &var.scalePower);
int scalePower = var->GetScalePower();
if (ImGui::InputInt("Scale Power (10^x)", &scalePower))
{
var->SetScalePower(scalePower);
}
// Modifier la valeur entière
int intValue = static_cast<int>(var.value);
int intValue = static_cast<int>(var->GetIntegerValue());
if (ImGui::InputInt("Integer Value", &intValue)) {
var.value = static_cast<int64_t>(intValue);
var->SetIntegerValue(static_cast<int64_t>(intValue));
}
// Afficher la valeur flottante calculée
float floatValue = ScaledToFloat(var.value, var.scalePower);
float floatValue = ScaledToFloat(var->GetIntegerValue(), var->GetScalePower());
ImGui::Text("Float Value: %.6f", floatValue);
}
else
{
std::strncpy(buffer, var.valueText.c_str(), sizeof(buffer));
std::strncpy(buffer, var->GetStringValue().c_str(), sizeof(buffer));
if (ImGui::InputText("Text value", buffer, sizeof(buffer)))
{
var.valueText = buffer;
var->SetTextValue(buffer);
}
}