Better custom AST builder using kahn algorithm

This commit is contained in:
anthony@rabine.fr 2025-04-22 16:35:50 +02:00
parent 5c16e2bd94
commit 3c1224e937
27 changed files with 568 additions and 599 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

@ -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

@ -2,19 +2,128 @@
#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
};
// 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;
}
// 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("[variable.h] GetValue(): Invalid value type requested");
}
}
using VariableValue = std::variant<int, float, bool, std::string>;
std::string GetUuid() const {
return m_uuid;
}
Variable() {
m_uuid = Uuid().String();
}
Variable (const std::string &name)
: Variable()
{
m_variableName = name;
}
private:
std::string m_variableName;
ValueType m_valueType;
VariableValue m_value;
bool m_isConstant;
std::string m_uuid;
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,10 @@ 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<PathTree>& roots, const std::vector<std::shared_ptr<Variable>> &variables)
{
m_roots = roots;
/*
Reset();
// Generate header comments
@ -68,11 +67,31 @@ public:
// Generate data section
StartSection(Section::DATA);
GenerateDataSection(nodes, roots);
GenerateDataSection(nodes, roots, variables);
// Generate text section
StartSection(Section::TEXT);
GenerateTextSection(roots);
*/
return m_assembly.str();
}
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
GenerateHeader();
// Generate data section
StartSection(Section::DATA);
GenerateDataSection(nodes, variables);
// Generate text section
StartSection(Section::TEXT);
GenerateTextSection(order);
return m_assembly.str();
}
@ -139,14 +158,19 @@ 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);
}
m_assembly << "\n\n";
// Generate all
/*
// Generate string literals
for (const auto& literal : m_stringLiterals) {
std::string label = "str_" + std::to_string(m_labelCounter++);
@ -158,12 +182,20 @@ private:
for (const auto& t : trees) {
CollectVariables(t.root);
}
*/
}
void GenerateTextSection(const std::vector<PathTree>& trees) {
void GenerateTextSection(const std::vector<std::shared_ptr<ASTNode>>& orderedNodes) {
// Program entry point
m_assembly << ".main:\n";
for (const auto& node : orderedNodes) {
GenerateNodeCode(node);
}
/*
// Process execution paths first
for (const auto& tree : trees) {
if (tree.isExecutionPath) {
@ -177,6 +209,7 @@ private:
GenerateNodeCode(tree.root, true);
}
}
*/
// Program exit
GenerateExit();
@ -187,11 +220,13 @@ private:
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,20 +21,23 @@ public:
if (m_context.debugOutput) {
AddComment("Node: " + node->node->GetTypeName() + " (ID: " + node->node->GetId() + ")");
}
/*
if (isDataPath)
{
if (node->IsType<OperatorNode>()) {
GenerateOperatorNode(node);
}
else if (node->IsType<VariableNode>()) {
GenerateVariableNode(node);
}
// else if (node->IsType<VariableNode>()) {
// GenerateVariableNode(node);
// }
}
else
{
if (node->IsType<FunctionEntryNode>()) {
*/
if (node->IsType<OperatorNode>()) {
GenerateOperatorNode(node);
}
else if (node->IsType<FunctionEntryNode>()) {
GenerateFunctionEntry(node);
}
else if (node->IsType<BranchNode>()) {
@ -43,7 +46,7 @@ public:
else if (node->IsType<PrintNode>()) {
GeneratePrintNode(node);
}
}
// }
// If we're processing a data path, traverse data outputs
if (isDataPath) {
@ -73,9 +76,9 @@ private:
AddComment("Function Entry");
m_depth++;
for (auto& child : node->children) {
GenerateNodeCode(child);
}
// for (auto& child : node->children) {
// GenerateNodeCode(child);
// }
m_depth--;
}
@ -92,14 +95,14 @@ private:
// 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() + ")");
*/
// Compare result and jump
m_assembly << " pop eax\n"
@ -127,15 +130,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,11 +152,12 @@ private:
AddComment("Operator: " + std::to_string(static_cast<int>(opNode->GetOperationType())));
m_depth++;
// Generate code for operands
// Generate code for variables
for (const auto& [port, inputNode] : node->dataInputs) {
GenerateNodeCode(inputNode);
// m_assembly << " load r0, " << inputNode.node->GetId() << "\n";
}
// Generate operator code
switch (opNode->GetOperationType()) {
case OperatorNode::OperationType::ADD:
@ -178,11 +186,91 @@ private:
<< " push eax\n";
break;
// Add other operators...
/*
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();
}
*/
}
m_depth--;
}
virtual void Visit(const std::shared_ptr<Variable> v) override
{
if (v->IsConstant())
{
if (v->GetValueType() == Variable::ValueType::STRING)
{
m_assembly << "$" << v->GetVariableName() << " DC8, \"" << v->GetValue<std::string>() << "\"\n";
}
else if (v->GetValueType() == Variable::ValueType::INTEGER)
{
m_assembly << "$" << v->GetVariableName() << " DC32, " << v->GetValue<int>() << "\n";
}
else if (v->GetValueType() == Variable::ValueType::FLOAT)
{
m_assembly << "$" << v->GetVariableName() << " DC32, " << v->GetValue<float>() << "\n";
}
else if (v->GetValueType() == Variable::ValueType::BOOL)
{
m_assembly << "$" << v->GetVariableName() << " DCB, " << (v->GetValue<bool>() ? "1" : "0") << "\n";
}
}
}
/*
void GenerateVariableNode(std::shared_ptr<ASTNode> node) {
auto* varNode = node->GetAs<VariableNode>();
if (!varNode) return;
@ -193,7 +281,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";
@ -150,82 +158,44 @@ public:
const std::vector<std::shared_ptr<Connection>>& connections)
: m_nodes(nodes), m_connections(connections) {}
std::vector<PathTree> BuildAST() {
// Create node map for quick lookups
std::unordered_map<std::string, std::shared_ptr<BaseNode>> nodeMap;
for (const auto& node : m_nodes) {
nodeMap[node->GetId()] = node;
}
// Find all root nodes (nodes without incoming execution connections)
std::unordered_set<std::string> hasIncomingExec;
std::unordered_set<std::string> hasIncomingData;
for (const auto& conn : m_connections) {
if (conn->type == Connection::EXECUTION_LINKJ) {
hasIncomingExec.insert(conn->inNodeId);
} else {
hasIncomingData.insert(conn->inNodeId);
}
}
// Collect root nodes
std::vector<std::shared_ptr<BaseNode>> execRoots;
std::vector<std::shared_ptr<BaseNode>> dataRoots;
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))
std::vector<std::shared_ptr<ASTNode>> BuildAST()
{
// Check if the node has any outgoing data connections
bool hasDataOutput = false;
// Create node map for quick lookups
std::unordered_map<std::string, std::shared_ptr<ASTNode>> nodeMap;
for (const auto& node : m_nodes) {
nodeMap[node->GetId()] = std::make_shared<ASTNode>(node);
}
// Build adjacency list for the nodes from the connections
for (const auto& conn : m_connections) {
// 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);
}
std::vector<std::shared_ptr<ASTNode>> topologicalOrder = ApplyKahnAlgorithm(nodeMap);
// Maintenant, on va ajouter les connexions de données
for (const auto& conn : m_connections)
{
if (conn->type == Connection::DATA_LINK &&
conn->outNodeId == node->GetId()) {
hasDataOutput = true;
break;
}
}
auto outNode = nodeMap[conn->outNodeId];
auto inNode = nodeMap[conn->inNodeId];
if (hasDataOutput && !(dynamic_cast<VariableNode*>(node.get())))
// Keep variables nodes as data inputs
if (dynamic_cast<VariableNode*>(outNode->node.get()))
{
dataRoots.push_back(node);
}
// if (hasDataOutput) {
// dataRoots.push_back(node);
// }
inNode->AddDataInput(conn->inPortIndex, outNode);
}
}
std::vector<PathTree> pathTrees;
return topologicalOrder;
// 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);
}
// 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:
@ -233,6 +203,89 @@ private:
const std::vector<std::shared_ptr<Connection>>& m_connections;
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
{
for (const auto& conn : m_connections)
@ -249,94 +302,6 @@ private:
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,10 @@ protected:
}
virtual void Visit(const std::shared_ptr<Variable> v) {
}
virtual void GenerateExit() {
@ -84,7 +88,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 +109,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);
int result = value1 + value2;
FlowVisualizer::PrintDataFlow("ADD", "Result",
std::to_string(result), 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);
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 +150,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

@ -128,3 +128,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
{
@ -70,10 +78,6 @@ public:
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 +101,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; }
@ -134,12 +147,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,15 +16,8 @@ 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;

View file

@ -13,7 +13,6 @@ public:
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 ""; }

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,15 +16,8 @@ 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;

View file

@ -13,7 +13,6 @@ public:
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

@ -32,12 +32,3 @@ std::string FunctionNode::Build(IStoryPage &page, const StoryOptions &options, i
return std::string();
}
std::string FunctionNode::GenerateConstants(IStoryPage &page, IStoryProject &project, int nb_out_conns)
{
std::string s;
return s;
}

View file

@ -13,7 +13,6 @@ public:
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,17 @@
#include "story_project.h"
#include "connection.h"
#include "sys_lib.h"
#include "compiler.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_variables[m_label] = v;
}
@ -17,41 +21,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

@ -32,12 +32,3 @@ std::string VariableNode::Build(IStoryPage &page, const StoryOptions &options, i
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,25 @@ 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 SetVariableUuid(const std::string &uuid) {
m_variableUuid = uuid;
}
void SetConstant(bool isConstant) {
m_isConstant = isConstant;
std::string GetVariableUuid() const {
return m_variableUuid;
}
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::string m_variableUuid;
};

View file

@ -52,11 +52,11 @@ 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)
{

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;
@ -98,20 +59,50 @@ TEST_CASE( "Check various indentations and typos" ) {
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");
auto variableNode2 = std::make_shared<VariableNode>("variable-node");
variableNode2->SetValue(10);
variableNode2->SetValueType(VariableNode::ValueType::INTEGER);
variableNode2->SetVariableName("Y");
std::vector<std::shared_ptr<Variable>> variables;
auto var1 = std::make_shared<Variable>("X");
var1->SetValue(5);
var1->SetValueType(Variable::ValueType::INTEGER);
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->SetVariableUuid(var1->GetUuid());
auto variableNodeY = std::make_shared<VariableNode>("variable-node");
variableNodeY->SetVariableUuid(var2->GetUuid());
auto variableNodeA = std::make_shared<VariableNode>("variable-node");
variableNodeA->SetVariableUuid(var3->GetUuid());
auto variableNodeB = std::make_shared<VariableNode>("variable-node");
variableNodeB->SetVariableUuid(var4->GetUuid());
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
@ -227,34 +229,19 @@ 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;
}
*/
// 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, pathTrees, variables);
std::cout << "\nGenerated flow:\n" << flow << std::endl;
// Generate assembly
std::string assembly = generator.GenerateAssembly(nodes, pathTrees);
std::string assembly = generator.GenerateAssembly(nodes, pathTrees, variables);
std::cout << "\nGenerated assembly:\n" << assembly << std::endl;
// compiler.generateAssembly();
// std::cout << compiler.GetCode() << std::endl;
// Chip32::Machine machine;
// machine.QuickExecute(compiler.GetCode());