mirror of
https://github.com/arabine/open-story-teller.git
synced 2025-12-06 17:09:06 +01:00
First version of AST/flow/assembly generation base classes, arch seems ok
This commit is contained in:
parent
b9a946eab4
commit
183e5d0727
29 changed files with 1806 additions and 644 deletions
|
|
@ -1,306 +0,0 @@
|
|||
#include <iostream>
|
||||
#include <vector>
|
||||
#include <cctype>
|
||||
#include <memory>
|
||||
#include <fstream>
|
||||
enum class TokenType {
|
||||
FUNC, IDENTIFIER, INT, CONST, VAR, IF, ELSE, WHILE, NUMBER, ASSIGN, PLUS, MINUS, MULT, DIV, SEMICOLON,
|
||||
LPAREN, RPAREN, LBRACE, RBRACE, END, GREATER, LESS, EQUAL, NOTEQUAL
|
||||
};
|
||||
|
||||
struct Token {
|
||||
TokenType type;
|
||||
std::string value;
|
||||
};
|
||||
|
||||
class Lexer {
|
||||
public:
|
||||
Lexer(const std::string& input) : input(input), pos(0) {}
|
||||
|
||||
std::vector<Token> tokenize() {
|
||||
std::vector<Token> tokens;
|
||||
|
||||
while (pos < input.size()) {
|
||||
if (std::isspace(input[pos])) {
|
||||
++pos;
|
||||
} else if (std::isalpha(input[pos])) {
|
||||
std::string ident = readIdentifier();
|
||||
if (ident == "func") {
|
||||
tokens.push_back({TokenType::FUNC, ident});
|
||||
} else if (ident == "int") {
|
||||
tokens.push_back({TokenType::INT, ident});
|
||||
} else if (ident == "const") {
|
||||
tokens.push_back({TokenType::CONST, ident});
|
||||
} else if (ident == "var") {
|
||||
tokens.push_back({TokenType::VAR, ident});
|
||||
} else if (ident == "if") {
|
||||
tokens.push_back({TokenType::IF, ident});
|
||||
} else if (ident == "else") {
|
||||
tokens.push_back({TokenType::ELSE, ident});
|
||||
} else if (ident == "while") {
|
||||
tokens.push_back({TokenType::WHILE, ident});
|
||||
} else {
|
||||
tokens.push_back({TokenType::IDENTIFIER, ident});
|
||||
}
|
||||
} else if (std::isdigit(input[pos])) {
|
||||
tokens.push_back({TokenType::NUMBER, readNumber()});
|
||||
} else {
|
||||
switch (input[pos]) {
|
||||
case '(': tokens.push_back({TokenType::LPAREN, "("}); break;
|
||||
case ')': tokens.push_back({TokenType::RPAREN, ")"}); break;
|
||||
case '{': tokens.push_back({TokenType::LBRACE, "{"}); break;
|
||||
case '}': tokens.push_back({TokenType::RBRACE, "}"}); break;
|
||||
case '=': tokens.push_back({TokenType::ASSIGN, "="}); break;
|
||||
case '+': tokens.push_back({TokenType::PLUS, "+"}); break;
|
||||
case '-': tokens.push_back({TokenType::MINUS, "-"}); break;
|
||||
case '*': tokens.push_back({TokenType::MULT, "*"}); break;
|
||||
case '/': tokens.push_back({TokenType::DIV, "/"}); break;
|
||||
case ';': tokens.push_back({TokenType::SEMICOLON, ";"}); break;
|
||||
case '>': tokens.push_back({TokenType::GREATER, ">"}); break;
|
||||
case '<': tokens.push_back({TokenType::LESS, "<"}); break;
|
||||
default: std::cerr << "Caractère inattendu : " << input[pos] << std::endl;
|
||||
}
|
||||
++pos;
|
||||
}
|
||||
}
|
||||
tokens.push_back({TokenType::END, ""});
|
||||
return tokens;
|
||||
}
|
||||
|
||||
private:
|
||||
std::string input;
|
||||
size_t pos;
|
||||
|
||||
std::string readIdentifier() {
|
||||
size_t start = pos;
|
||||
while (pos < input.size() && std::isalnum(input[pos])) ++pos;
|
||||
return input.substr(start, pos - start);
|
||||
}
|
||||
|
||||
std::string readNumber() {
|
||||
size_t start = pos;
|
||||
while (pos < input.size() && std::isdigit(input[pos])) ++pos;
|
||||
return input.substr(start, pos - start);
|
||||
}
|
||||
};
|
||||
|
||||
// AST Nodes
|
||||
struct ASTNode {
|
||||
virtual ~ASTNode() = default;
|
||||
virtual void print(int indent = 0) const = 0;
|
||||
virtual void generateAssembly(std::ofstream& out) const = 0;
|
||||
};
|
||||
|
||||
struct ExpressionNode : public ASTNode {
|
||||
std::string value;
|
||||
ExpressionNode(std::string value) : value(std::move(value)) {}
|
||||
|
||||
void print(int indent = 0) const override {
|
||||
std::cout << std::string(indent, ' ') << "Number: " << value << std::endl;
|
||||
}
|
||||
|
||||
void generateAssembly(std::ofstream& out) const override {
|
||||
out << "\tmov eax, " << value << "\n";
|
||||
}
|
||||
};
|
||||
|
||||
struct BinaryOpNode : public ASTNode {
|
||||
std::unique_ptr<ASTNode> left;
|
||||
std::unique_ptr<ASTNode> right;
|
||||
TokenType op;
|
||||
|
||||
BinaryOpNode(std::unique_ptr<ASTNode> left, TokenType op, std::unique_ptr<ASTNode> right)
|
||||
: left(std::move(left)), op(op), right(std::move(right)) {}
|
||||
|
||||
void print(int indent = 0) const override {
|
||||
std::cout << std::string(indent, ' ') << "BinaryOp: ";
|
||||
left->print(indent + 2);
|
||||
std::cout << std::string(indent + 2, ' ') << (op == TokenType::PLUS ? "+" :
|
||||
op == TokenType::MINUS ? "-" :
|
||||
op == TokenType::MULT ? "*" : "/") << std::endl;
|
||||
right->print(indent + 2);
|
||||
}
|
||||
|
||||
void generateAssembly(std::ofstream& out) const override {
|
||||
left->generateAssembly(out);
|
||||
out << "\tpush eax\n";
|
||||
right->generateAssembly(out);
|
||||
out << "\tpop ebx\n";
|
||||
|
||||
switch (op) {
|
||||
case TokenType::PLUS: out << "\tadd eax, ebx\n"; break;
|
||||
case TokenType::MINUS: out << "\tsub eax, ebx\n"; break;
|
||||
case TokenType::MULT: out << "\timul eax, ebx\n"; break;
|
||||
case TokenType::DIV: out << "\tcdq\n\tidiv ebx\n"; break;
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
struct AssignmentNode : public ASTNode {
|
||||
std::string varName;
|
||||
std::unique_ptr<ASTNode> expression;
|
||||
|
||||
AssignmentNode(std::string varName, std::unique_ptr<ASTNode> expression)
|
||||
: varName(std::move(varName)), expression(std::move(expression)) {}
|
||||
|
||||
void print(int indent = 0) const override {
|
||||
std::cout << std::string(indent, ' ') << "Assignment: " << varName << " = " << std::endl;
|
||||
expression->print(indent + 2);
|
||||
}
|
||||
|
||||
void generateAssembly(std::ofstream& out) const override {
|
||||
expression->generateAssembly(out);
|
||||
out << "\tmov [" << varName << "], eax\n";
|
||||
}
|
||||
};
|
||||
|
||||
struct FunctionNode : public ASTNode {
|
||||
std::string name;
|
||||
std::vector<std::unique_ptr<ASTNode>> body;
|
||||
|
||||
FunctionNode(std::string name) : name(std::move(name)) {}
|
||||
|
||||
void print(int indent = 0) const override {
|
||||
std::cout << "Function: " << name << std::endl;
|
||||
for (const auto& stmt : body) {
|
||||
stmt->print(indent + 2);
|
||||
}
|
||||
}
|
||||
|
||||
void generateAssembly(std::ofstream& out) const override {
|
||||
out << "." << name << ":\n";
|
||||
for (const auto& stmt : body) {
|
||||
stmt->generateAssembly(out);
|
||||
}
|
||||
out << "\tret\n";
|
||||
}
|
||||
};
|
||||
|
||||
struct StatementNode : public ASTNode {
|
||||
|
||||
};
|
||||
|
||||
|
||||
struct VariableDeclarationNode : public StatementNode {
|
||||
std::string name;
|
||||
std::unique_ptr<ExpressionNode> value;
|
||||
|
||||
void print(int indent = 0) const override {
|
||||
std::cout << std::string(indent, ' ') << "VarDecl: " << name << std::endl;
|
||||
}
|
||||
|
||||
void generateAssembly(std::ofstream& out) const override {}
|
||||
};
|
||||
|
||||
struct IfNode : public StatementNode {
|
||||
std::unique_ptr<ExpressionNode> condition;
|
||||
std::vector<std::unique_ptr<ASTNode>> thenBranch;
|
||||
std::vector<std::unique_ptr<ASTNode>> elseBranch;
|
||||
|
||||
void print(int indent = 0) const override {
|
||||
std::cout << std::string(indent, ' ') << "If Statement" << std::endl;
|
||||
}
|
||||
|
||||
void generateAssembly(std::ofstream& out) const override {}
|
||||
};
|
||||
|
||||
struct WhileNode : public StatementNode {
|
||||
std::unique_ptr<ExpressionNode> condition;
|
||||
std::vector<std::unique_ptr<ASTNode>> body;
|
||||
|
||||
void print(int indent = 0) const override {
|
||||
std::cout << std::string(indent, ' ') << "While Statement" << std::endl;
|
||||
}
|
||||
|
||||
void generateAssembly(std::ofstream& out) const override {}
|
||||
};
|
||||
|
||||
|
||||
|
||||
class Parser {
|
||||
public:
|
||||
Parser(const std::vector<Token>& tokens) : tokens(tokens), pos(0) {}
|
||||
|
||||
std::unique_ptr<ASTNode> parse() {
|
||||
if (match(TokenType::FUNC)) {
|
||||
consume(TokenType::FUNC);
|
||||
std::string funcName = consume(TokenType::IDENTIFIER).value;
|
||||
consume(TokenType::LPAREN);
|
||||
consume(TokenType::RPAREN);
|
||||
consume(TokenType::LBRACE);
|
||||
std::vector<std::unique_ptr<StatementNode>> body;
|
||||
while (!match(TokenType::RBRACE)) {
|
||||
body.push_back(parseStatement());
|
||||
}
|
||||
consume(TokenType::RBRACE);
|
||||
return std::make_unique<FunctionNode>(funcName);
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
private:
|
||||
const std::vector<Token>& tokens;
|
||||
size_t pos;
|
||||
|
||||
bool match(TokenType type) {
|
||||
return pos < tokens.size() && tokens[pos].type == type;
|
||||
}
|
||||
|
||||
Token consume(TokenType type) {
|
||||
if (match(type)) {
|
||||
return tokens[pos++];
|
||||
}
|
||||
std::cout << "type: " << (int)type << std::endl;
|
||||
throw std::runtime_error("Unexpected token: expected " + std::to_string(static_cast<int>(type)) +
|
||||
", got " + std::to_string(static_cast<int>(tokens[pos].type)) +
|
||||
" (" + tokens[pos].value + ")");
|
||||
}
|
||||
|
||||
std::unique_ptr<StatementNode> parseStatement() { return nullptr; }
|
||||
};
|
||||
|
||||
int main() {
|
||||
std::string code = R"(
|
||||
|
||||
def MaFonction()
|
||||
var x
|
||||
x = 5 + 3
|
||||
|
||||
|
||||
)";
|
||||
Lexer lexer(code);
|
||||
std::vector<Token> tokens = lexer.tokenize();
|
||||
|
||||
for (const auto& token : tokens) {
|
||||
std::cout << "Token: " << static_cast<int>(token.type) << " (" << token.value << ")\n";
|
||||
}
|
||||
|
||||
|
||||
try {
|
||||
Parser parser(tokens);
|
||||
std::unique_ptr<ASTNode> ast = parser.parse();
|
||||
|
||||
|
||||
|
||||
if (ast) {
|
||||
ast->print();
|
||||
|
||||
std::ofstream outFile("output.asm");
|
||||
if (outFile.is_open()) {
|
||||
ast->generateAssembly(outFile);
|
||||
outFile.close();
|
||||
std::cout << "Code assembleur généré dans output.asm" << std::endl;
|
||||
} else {
|
||||
std::cerr << "Erreur lors de l'ouverture du fichier" << std::endl;
|
||||
}
|
||||
} else {
|
||||
std::cerr << "Erreur de parsing" << std::endl;
|
||||
}
|
||||
|
||||
} catch(const std::exception &e) {
|
||||
std::cerr << "Erreur de parsing: " << e.what() << std::endl;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -1,250 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include "sys_lib.h"
|
||||
#include "i_story_project.h"
|
||||
#include "base_node.h"
|
||||
#include "connection.h"
|
||||
|
||||
struct AstNode
|
||||
{
|
||||
std::shared_ptr<BaseNode> node; // pointeur vers le noeud en cours
|
||||
|
||||
std::vector<std::shared_ptr<AstNode>> inputs;
|
||||
std::shared_ptr<AstNode> condition; // Pour les boucles et les branchements
|
||||
std::vector<std::shared_ptr<AstNode>> body; // Pour les boucles et les branchements
|
||||
std::shared_ptr<AstNode> elseBody; // Pour les branchements
|
||||
|
||||
std::pair<int, int> position;
|
||||
};
|
||||
|
||||
|
||||
// AST (Arbre de syntaxe abstraite)
|
||||
struct AST {
|
||||
|
||||
std::map<std::string, int> variables; // Stockage des variables (adresses mémoire)
|
||||
std::string assemblyCode;
|
||||
int labelCounter = 0;
|
||||
std::map<std::string, std::shared_ptr<AstNode>> nodeMap;
|
||||
|
||||
// Fonction pour générer un label unique
|
||||
std::string generateLabel(AST& ast) {
|
||||
return "label" + std::to_string(ast.labelCounter++);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
class Compiler
|
||||
{
|
||||
|
||||
public:
|
||||
Compiler() = default;
|
||||
~Compiler() = default;
|
||||
|
||||
|
||||
static std::string FileToConstant(const std::string &FileName, const std::string &extension, IStoryProject &project);
|
||||
|
||||
|
||||
// Fonction pour construire l'AST à partir des nœuds et des connexions
|
||||
void buildAST(std::vector<std::shared_ptr<BaseNode>>& nodes, std::vector<std::shared_ptr<Connection>>& connections) {
|
||||
|
||||
m_ast.nodeMap.clear();
|
||||
|
||||
// Créer une map pour accéder aux nœuds par ID
|
||||
int x = 0, y = 0;
|
||||
for (auto& node : nodes) {
|
||||
auto astNode = std::make_shared<AstNode>();
|
||||
|
||||
astNode->position = {x, y};
|
||||
x += 1; // Espacement horizontal
|
||||
if (x > 20) { // Nouvelle ligne
|
||||
x = 0;
|
||||
y += 1; // Espacement vertical
|
||||
}
|
||||
|
||||
astNode->node = node;
|
||||
m_ast.nodeMap[node->GetId()] = astNode;
|
||||
}
|
||||
|
||||
// Construire les connexions entre les nœuds
|
||||
for (auto& connection : connections) {
|
||||
std::shared_ptr<AstNode> outNode = m_ast.nodeMap[connection->outNodeId];
|
||||
std::shared_ptr<AstNode> inNode = m_ast.nodeMap[connection->inNodeId];
|
||||
inNode->inputs.push_back(outNode); // Ajoute le noeud de sortie en entrée du noeud d'entrée
|
||||
}
|
||||
|
||||
// Gestion des conditions et des corps de boucles/branches
|
||||
for (auto& node : nodes) {
|
||||
std::string nodeType = node->GetTypeName();
|
||||
if (nodeType == "LOOP" || nodeType == "BRANCH") {
|
||||
for (auto& connection : connections) {
|
||||
if (connection->inNodeId == node->GetId()) {
|
||||
if (connection->inPortIndex == 0) { // Condition
|
||||
m_ast.nodeMap[node->GetId()]->condition = m_ast.nodeMap[connection->outNodeId];
|
||||
} else if (connection->inPortIndex == 1) { // Body
|
||||
m_ast.nodeMap[node->GetId()]->body.push_back(m_ast.nodeMap[connection->outNodeId]);
|
||||
} else if (nodeType == "BRANCH" && connection->inPortIndex == 2) { // ElseBody
|
||||
// if (m_ast.nodeMap[node->GetId()]->elseBody == nullptr) {
|
||||
// auto dummyNode = std::make_shared<BaseNode>("DummyNode");
|
||||
// auto dummyAstNode = std::make_shared<AstNode>();
|
||||
// dummyAstNode->node = dummyNode;
|
||||
// m_ast.nodeMap[node->GetId()]->elseBody = dummyAstNode;
|
||||
// }
|
||||
m_ast.nodeMap[node->GetId()]->elseBody->body.push_back(m_ast.nodeMap[connection->outNodeId]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
void displayNodeSchema() {
|
||||
// Définir une taille fixe pour la grille
|
||||
const int gridWidth = 20; // 20 cases de large
|
||||
int gridHeight = 0;
|
||||
|
||||
// Calculer la hauteur nécessaire pour la grille
|
||||
for (const auto& astNode : m_ast.nodeMap) {
|
||||
gridHeight = std::max(gridHeight, astNode.second->position.second + 1);
|
||||
}
|
||||
|
||||
// Créer une grille pour la console
|
||||
std::vector<std::vector<std::string>> grid(gridHeight, std::vector<std::string>(gridWidth, " "));
|
||||
|
||||
// Placer les nœuds dans la grille
|
||||
for (const auto& astNode : m_ast.nodeMap) {
|
||||
auto pos = astNode.second->position;
|
||||
std::string nodeName = astNode.second->node->GetTypeName();
|
||||
|
||||
// Tronquer le nom du nœud à 3 caractères
|
||||
if (nodeName.length() > 3) {
|
||||
nodeName = nodeName.substr(0, 3);
|
||||
} else {
|
||||
// Ajouter des espaces pour centrer le nom dans la case
|
||||
nodeName.insert(nodeName.end(), 3 - nodeName.length(), ' ');
|
||||
}
|
||||
|
||||
// Placer le nom du nœud dans la grille
|
||||
grid[pos.second][pos.first] = nodeName;
|
||||
}
|
||||
|
||||
// Dessiner les connexions
|
||||
for (const auto& astNode : m_ast.nodeMap) {
|
||||
for (const auto& input : astNode.second->inputs) {
|
||||
auto outPos = input->position;
|
||||
auto inPos = astNode.second->position;
|
||||
|
||||
// Dessiner une ligne entre les nœuds
|
||||
int startX = outPos.first + 1; // Commencer après le nom du nœud
|
||||
int startY = outPos.second;
|
||||
int endX = inPos.first;
|
||||
int endY = inPos.second;
|
||||
|
||||
// Ligne horizontale
|
||||
for (int i = startX; i <= endX; ++i) {
|
||||
if (i < gridWidth) {
|
||||
grid[startY][i][0] = '-'; // Dessiner la ligne dans la première position de la case
|
||||
}
|
||||
}
|
||||
|
||||
// Ligne verticale
|
||||
for (int i = std::min(startY, endY); i <= std::max(startY, endY); ++i) {
|
||||
if (i < gridHeight) {
|
||||
grid[i][endX][0] = '|'; // Dessiner la ligne dans la première position de la case
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Afficher la grille avec des séparateurs verticaux
|
||||
for (const auto& row : grid) {
|
||||
for (size_t i = 0; i < row.size(); ++i) {
|
||||
std::cout << row[i];
|
||||
if (i < row.size() - 1) {
|
||||
std::cout << "|"; // Séparateur vertical
|
||||
}
|
||||
}
|
||||
std::cout << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
void generateAssembly() {
|
||||
std::string assemblyCode;
|
||||
std::map<std::string, std::string> labels;
|
||||
|
||||
// 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";
|
||||
|
||||
// Générer des labels uniques pour les nœuds
|
||||
for (const auto& nodePair : m_ast.nodeMap) {
|
||||
std::string label = m_ast.generateLabel(m_ast);
|
||||
labels[nodePair.first] = label;
|
||||
|
||||
// Générer du code pour chaque type de nœud
|
||||
std::string nodeType = nodePair.second->node->GetTypeName();
|
||||
if (nodeType == "LOOP") {
|
||||
// Générer du code pour une boucle
|
||||
assemblyCode += " ; Loop start\n";
|
||||
// Ajouter des instructions pour la condition de la boucle
|
||||
if (nodePair.second->condition) {
|
||||
assemblyCode += " cmp_eq r0, " + labels[nodePair.second->condition->node->GetId()] + ", 0\n";
|
||||
assemblyCode += " skipz r0\n";
|
||||
assemblyCode += " jump " + labels[nodePair.second->body.front()->node->GetId()] + "\n";
|
||||
}
|
||||
// Ajouter des instructions pour le corps de la boucle
|
||||
for (const auto& bodyNode : nodePair.second->body) {
|
||||
assemblyCode += " ; Loop body\n";
|
||||
// Ajouter des instructions pour chaque nœud du corps
|
||||
}
|
||||
assemblyCode += " jump " + label + "\n"; // Retour au début de la boucle
|
||||
} else if (nodeType == "BRANCH") {
|
||||
// Générer du code pour un branchement
|
||||
assemblyCode += " ; Branch start\n";
|
||||
// Ajouter des instructions pour la condition du branchement
|
||||
if (nodePair.second->condition) {
|
||||
assemblyCode += " cmp_eq r0, " + labels[nodePair.second->condition->node->GetId()] + ", 0\n";
|
||||
assemblyCode += " skipnz r0\n";
|
||||
assemblyCode += " jump " + labels[nodePair.second->body.front()->node->GetId()] + "\n";
|
||||
}
|
||||
// Ajouter des instructions pour le corps du branchement
|
||||
for (const auto& bodyNode : nodePair.second->body) {
|
||||
assemblyCode += " ; Branch body\n";
|
||||
// Ajouter des instructions pour chaque nœud du corps
|
||||
}
|
||||
// Ajouter des instructions pour le corps du else
|
||||
if (nodePair.second->elseBody) {
|
||||
assemblyCode += " ; Else body\n";
|
||||
// Ajouter des instructions pour chaque nœud du corps du else
|
||||
}
|
||||
} else {
|
||||
// Générer du code pour d'autres types de nœuds
|
||||
assemblyCode += " ; Other node type: " + nodeType + "\n";
|
||||
assemblyCode += nodePair.second->node->GenerateAssembly();
|
||||
|
||||
// Ajouter des instructions spécifiques au type de nœud
|
||||
}
|
||||
}
|
||||
|
||||
// Ajouter le code assembleur généré à l'AST
|
||||
m_ast.assemblyCode = assemblyCode;
|
||||
}
|
||||
|
||||
std::string GetCode() const {
|
||||
return m_ast.assemblyCode;
|
||||
}
|
||||
|
||||
private:
|
||||
AST m_ast;
|
||||
|
||||
};
|
||||
363
core/story-manager/src/compiler/assembly_generator.h
Normal file
363
core/story-manager/src/compiler/assembly_generator.h
Normal file
|
|
@ -0,0 +1,363 @@
|
|||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <sstream>
|
||||
#include <unordered_map>
|
||||
#include <stack>
|
||||
#include <vector>
|
||||
#include <iomanip>
|
||||
#include <chrono>
|
||||
#include <ctime>
|
||||
|
||||
#include "ast_builder.h"
|
||||
#include "print_node.h"
|
||||
#include "function_entry_node.h"
|
||||
#include "variable_node.h"
|
||||
#include "branch_node.h"
|
||||
#include "operator_node.h"
|
||||
|
||||
class AssemblyGenerator {
|
||||
public:
|
||||
struct GeneratorContext {
|
||||
std::string timestamp;
|
||||
std::string username;
|
||||
bool debugOutput;
|
||||
bool optimizeCode;
|
||||
int stackSize;
|
||||
|
||||
GeneratorContext(const std::string& ts = "2025-04-08 12:09:01",
|
||||
const std::string& user = "arabine",
|
||||
bool debug = true,
|
||||
bool optimize = true,
|
||||
int stack = 1024)
|
||||
: timestamp(ts)
|
||||
, username(user)
|
||||
, debugOutput(debug)
|
||||
, optimizeCode(optimize)
|
||||
, stackSize(stack)
|
||||
{}
|
||||
};
|
||||
|
||||
AssemblyGenerator(const GeneratorContext& context = GeneratorContext())
|
||||
: m_context(context)
|
||||
{
|
||||
Reset();
|
||||
}
|
||||
|
||||
void Reset() {
|
||||
m_assembly.str("");
|
||||
m_labelCounter = 0;
|
||||
m_variableAddresses.clear();
|
||||
m_currentStackOffset = 0;
|
||||
m_stringLiterals.clear();
|
||||
m_depth = 0;
|
||||
m_currentSection = Section::NONE;
|
||||
}
|
||||
|
||||
std::string GenerateAssembly(const std::vector<PathTree>& roots) {
|
||||
Reset();
|
||||
|
||||
// Generate header comments
|
||||
GenerateHeader();
|
||||
|
||||
// Generate data section
|
||||
StartSection(Section::DATA);
|
||||
GenerateDataSection(roots);
|
||||
|
||||
// Generate bss section for uninitialized data
|
||||
StartSection(Section::BSS);
|
||||
GenerateBssSection();
|
||||
|
||||
// Generate text section
|
||||
StartSection(Section::TEXT);
|
||||
GenerateTextSection(roots);
|
||||
|
||||
return m_assembly.str();
|
||||
}
|
||||
|
||||
protected:
|
||||
enum class Section {
|
||||
NONE,
|
||||
DATA,
|
||||
BSS,
|
||||
TEXT
|
||||
};
|
||||
|
||||
virtual void GenerateNodeCode(std::shared_ptr<ASTNode> node, bool isDataPath = false) {
|
||||
if (!node) return;
|
||||
|
||||
if (m_context.debugOutput) {
|
||||
AddComment("Node: " + node->node->GetTypeName() + " (ID: " + node->node->GetId() + ")");
|
||||
}
|
||||
|
||||
if (node->IsType<FunctionEntryNode>()) {
|
||||
GenerateFunctionEntry(node);
|
||||
}
|
||||
else if (node->IsType<BranchNode>()) {
|
||||
GenerateBranchNode(node);
|
||||
}
|
||||
else if (node->IsType<PrintNode>()) {
|
||||
GeneratePrintNode(node);
|
||||
}
|
||||
else if (node->IsType<OperatorNode>()) {
|
||||
GenerateOperatorNode(node);
|
||||
}
|
||||
else if (node->IsType<VariableNode>()) {
|
||||
GenerateVariableNode(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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
GeneratorContext m_context;
|
||||
std::stringstream m_assembly;
|
||||
int m_labelCounter;
|
||||
std::unordered_map<std::string, std::string> m_variableAddresses;
|
||||
int m_currentStackOffset;
|
||||
std::vector<std::string> m_stringLiterals;
|
||||
int m_depth;
|
||||
Section m_currentSection;
|
||||
|
||||
void GenerateHeader() {
|
||||
m_assembly << "; Assembly generated by Visual Node Editor\n"
|
||||
<< "; Generation time: " << m_context.timestamp << "\n"
|
||||
<< "; Generated by: " << m_context.username << "\n"
|
||||
<< "; Optimization: " << (m_context.optimizeCode ? "enabled" : "disabled") << "\n"
|
||||
<< "\n";
|
||||
}
|
||||
|
||||
void StartSection(Section section) {
|
||||
if (m_currentSection == section) return;
|
||||
|
||||
m_currentSection = section;
|
||||
switch (section) {
|
||||
case Section::DATA:
|
||||
m_assembly << "section .data\n";
|
||||
break;
|
||||
case Section::BSS:
|
||||
m_assembly << "section .bss\n";
|
||||
break;
|
||||
case Section::TEXT:
|
||||
m_assembly << "section .text\n"
|
||||
<< "global _start\n\n";
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void GenerateDataSection(const std::vector<PathTree>& trees) {
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
|
||||
void GenerateBssSection() {
|
||||
m_assembly << " stack_space resb " << m_context.stackSize << "\n";
|
||||
}
|
||||
|
||||
void GenerateTextSection(const std::vector<PathTree>& trees) {
|
||||
// Program entry point
|
||||
m_assembly << "_start:\n";
|
||||
|
||||
// Setup stack frame
|
||||
m_assembly << " mov ebp, esp\n"
|
||||
<< " sub esp, " << m_context.stackSize << "\n\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);
|
||||
}
|
||||
}
|
||||
|
||||
// Program exit
|
||||
GenerateExit();
|
||||
}
|
||||
|
||||
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
|
||||
if (auto conditionNode = node->GetDataInput(0)) {
|
||||
GenerateNodeCode(conditionNode);
|
||||
}
|
||||
|
||||
// Compare result and jump
|
||||
m_assembly << " pop eax\n"
|
||||
<< " cmp eax, 0\n"
|
||||
<< " je " << labelFalse << "\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--;
|
||||
}
|
||||
|
||||
void GeneratePrintNode(std::shared_ptr<ASTNode> node) {
|
||||
auto* printNode = node->GetAs<PrintNode>();
|
||||
if (!printNode) return;
|
||||
|
||||
std::string text = printNode->GetText();
|
||||
std::string label = AddStringLiteral(text);
|
||||
|
||||
// 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) {
|
||||
auto* opNode = node->GetAs<OperatorNode>();
|
||||
if (!opNode) return;
|
||||
|
||||
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 operator code
|
||||
switch (opNode->GetOperationType()) {
|
||||
case OperatorNode::OperationType::ADD:
|
||||
m_assembly << " pop ebx\n"
|
||||
<< " pop eax\n"
|
||||
<< " add eax, ebx\n"
|
||||
<< " push eax\n";
|
||||
break;
|
||||
case OperatorNode::OperationType::SUBTRACT:
|
||||
m_assembly << " pop ebx\n"
|
||||
<< " pop eax\n"
|
||||
<< " sub eax, ebx\n"
|
||||
<< " push eax\n";
|
||||
break;
|
||||
case OperatorNode::OperationType::MULTIPLY:
|
||||
m_assembly << " pop ebx\n"
|
||||
<< " pop eax\n"
|
||||
<< " imul eax, ebx\n"
|
||||
<< " push eax\n";
|
||||
break;
|
||||
case OperatorNode::OperationType::DIVIDE:
|
||||
m_assembly << " pop ebx\n"
|
||||
<< " pop eax\n"
|
||||
<< " cdq\n"
|
||||
<< " idiv ebx\n"
|
||||
<< " push eax\n";
|
||||
break;
|
||||
// Add other operators...
|
||||
}
|
||||
|
||||
m_depth--;
|
||||
}
|
||||
|
||||
void GenerateVariableNode(std::shared_ptr<ASTNode> node) {
|
||||
auto* varNode = node->GetAs<VariableNode>();
|
||||
if (!varNode) return;
|
||||
|
||||
std::string varName = varNode->GetVariableName();
|
||||
|
||||
AddComment("Load variable: " + varName);
|
||||
m_assembly << " mov eax, [" << m_variableAddresses[varName] << "]\n"
|
||||
<< " push eax\n";
|
||||
}
|
||||
|
||||
void GenerateExit() {
|
||||
AddComment("Program exit");
|
||||
m_assembly << " mov eax, 1\n" // sys_exit
|
||||
<< " mov ebx, 0\n" // return 0
|
||||
<< " int 0x80\n";
|
||||
}
|
||||
|
||||
void CollectVariables(std::shared_ptr<ASTNode> node) {
|
||||
if (!node) return;
|
||||
|
||||
if (node->IsType<VariableNode>()) {
|
||||
auto* varNode = node->GetAs<VariableNode>();
|
||||
std::string varName = varNode->GetVariableName();
|
||||
if (m_variableAddresses.find(varName) == m_variableAddresses.end()) {
|
||||
m_variableAddresses[varName] = varName;
|
||||
m_assembly << varName << ":\n" << varNode->GenerateAssembly();
|
||||
}
|
||||
}
|
||||
|
||||
// Traverse children
|
||||
for (auto& child : node->children) {
|
||||
CollectVariables(child);
|
||||
}
|
||||
|
||||
// Traverse data inputs
|
||||
for (const auto& [port, inputNode] : node->dataInputs) {
|
||||
CollectVariables(inputNode);
|
||||
}
|
||||
}
|
||||
|
||||
std::string AddStringLiteral(const std::string& text) {
|
||||
std::string label = "str_" + std::to_string(m_stringLiterals.size());
|
||||
m_stringLiterals.push_back(text);
|
||||
return label;
|
||||
}
|
||||
|
||||
std::string GenerateUniqueLabel(const std::string& prefix) {
|
||||
return prefix + "_" + std::to_string(m_labelCounter++);
|
||||
}
|
||||
|
||||
void AddComment(const std::string& comment) {
|
||||
if (m_context.debugOutput) {
|
||||
m_assembly << std::string(m_depth * 4, ' ') << "; " << comment << "\n";
|
||||
}
|
||||
}
|
||||
};
|
||||
293
core/story-manager/src/compiler/ast_builder.h
Normal file
293
core/story-manager/src/compiler/ast_builder.h
Normal file
|
|
@ -0,0 +1,293 @@
|
|||
#pragma once
|
||||
|
||||
|
||||
#include "base_node.h"
|
||||
#include "connection.h"
|
||||
#include "function_entry_node.h"
|
||||
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
#include <queue>
|
||||
|
||||
class ASTNode {
|
||||
public:
|
||||
// Structure to represent a data connection target
|
||||
struct DataTarget {
|
||||
std::shared_ptr<ASTNode> node;
|
||||
unsigned int portIndex;
|
||||
};
|
||||
|
||||
explicit ASTNode(std::shared_ptr<BaseNode> node)
|
||||
: node(node) {}
|
||||
|
||||
// The actual node from the visual editor
|
||||
std::shared_ptr<BaseNode> node;
|
||||
|
||||
// Execution flow children (for EXECUTION_LINKJ connections)
|
||||
std::vector<std::shared_ptr<ASTNode>> children;
|
||||
|
||||
// Data inputs: port_index -> source node
|
||||
std::unordered_map<unsigned int, std::shared_ptr<ASTNode>> dataInputs;
|
||||
|
||||
// Data outputs: output_port_index -> vector of (target node, target port)
|
||||
std::unordered_map<unsigned int, std::vector<DataTarget>> dataOutputs;
|
||||
|
||||
// Helper methods
|
||||
bool HasDataInput(unsigned int portIndex) const {
|
||||
return dataInputs.find(portIndex) != dataInputs.end();
|
||||
}
|
||||
|
||||
bool HasDataOutput(unsigned int portIndex) const {
|
||||
return dataOutputs.find(portIndex) != dataOutputs.end();
|
||||
}
|
||||
|
||||
std::shared_ptr<ASTNode> GetDataInput(unsigned int portIndex) const {
|
||||
auto it = dataInputs.find(portIndex);
|
||||
if (it != dataInputs.end()) {
|
||||
return it->second;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const std::vector<DataTarget>& GetDataOutputs(unsigned int portIndex) const {
|
||||
static const std::vector<DataTarget> empty;
|
||||
auto it = dataOutputs.find(portIndex);
|
||||
if (it != dataOutputs.end()) {
|
||||
return it->second;
|
||||
}
|
||||
return empty;
|
||||
}
|
||||
|
||||
// Add a data input connection
|
||||
void AddDataInput(unsigned int portIndex, std::shared_ptr<ASTNode> sourceNode) {
|
||||
dataInputs[portIndex] = sourceNode;
|
||||
}
|
||||
|
||||
// Add a data output connection
|
||||
void AddDataOutput(unsigned int sourcePort, std::shared_ptr<ASTNode> targetNode, unsigned int targetPort) {
|
||||
DataTarget target{targetNode, targetPort};
|
||||
dataOutputs[sourcePort].push_back(target);
|
||||
}
|
||||
|
||||
// Add an execution child
|
||||
void AddChild(std::shared_ptr<ASTNode> child) {
|
||||
children.push_back(child);
|
||||
}
|
||||
|
||||
// Get execution path child count
|
||||
size_t GetChildCount() const {
|
||||
return children.size();
|
||||
}
|
||||
|
||||
// Get child at specific index
|
||||
std::shared_ptr<ASTNode> GetChild(size_t index) const {
|
||||
if (index < children.size()) {
|
||||
return children[index];
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Utility method to check if this is a specific node type
|
||||
template<typename T>
|
||||
bool IsType() const {
|
||||
return dynamic_cast<const T*>(node.get()) != nullptr;
|
||||
}
|
||||
|
||||
// Get node as specific type
|
||||
template<typename T>
|
||||
T* GetAs() const {
|
||||
return dynamic_cast<T*>(node.get());
|
||||
}
|
||||
|
||||
// Debug information
|
||||
std::string GetDebugString() const {
|
||||
std::string result = "Node: " + node->GetTypeName() + " (ID: " + node->GetId() + ")\n";
|
||||
|
||||
// Add data inputs info
|
||||
for (const auto& [port, input] : dataInputs) {
|
||||
result += " Input port " + std::to_string(port) +
|
||||
" <- " + input->node->GetTypeName() + "\n";
|
||||
}
|
||||
|
||||
// Add data outputs info
|
||||
for (const auto& [port, outputs] : dataOutputs) {
|
||||
for (const auto& target : outputs) {
|
||||
result += " Output port " + std::to_string(port) +
|
||||
" -> " + target.node->node->GetTypeName() +
|
||||
" (port " + std::to_string(target.portIndex) + ")\n";
|
||||
}
|
||||
}
|
||||
|
||||
// Add execution children info
|
||||
for (size_t i = 0; i < children.size(); ++i) {
|
||||
result += " Child " + std::to_string(i) + ": " +
|
||||
children[i]->node->GetTypeName() + "\n";
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
struct PathTree {
|
||||
std::shared_ptr<ASTNode> root;
|
||||
std::vector<std::shared_ptr<Connection>> connections;
|
||||
bool isExecutionPath; // true for main flow, false for input paths
|
||||
};
|
||||
|
||||
class ASTBuilder {
|
||||
public:
|
||||
|
||||
ASTBuilder(const std::vector<std::shared_ptr<BaseNode>>& nodes,
|
||||
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()) {
|
||||
// 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) {
|
||||
dataRoots.push_back(node);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<PathTree> pathTrees;
|
||||
|
||||
// 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:
|
||||
const std::vector<std::shared_ptr<BaseNode>>& m_nodes;
|
||||
const std::vector<std::shared_ptr<Connection>>& m_connections;
|
||||
|
||||
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;
|
||||
|
||||
while (!queue.empty()) {
|
||||
auto current = queue.front();
|
||||
queue.pop();
|
||||
|
||||
if (visited.find(current->node->GetId()) != visited.end()) {
|
||||
continue;
|
||||
}
|
||||
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);
|
||||
current->dataOutputs[conn->outPortIndex].push_back(
|
||||
{childNode, conn->inPortIndex});
|
||||
queue.push(childNode);
|
||||
tree.connections.push_back(conn);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
442
core/story-manager/src/compiler/compiler.h
Normal file
442
core/story-manager/src/compiler/compiler.h
Normal file
|
|
@ -0,0 +1,442 @@
|
|||
#pragma once
|
||||
|
||||
#include "sys_lib.h"
|
||||
#include "i_story_project.h"
|
||||
#include "base_node.h"
|
||||
#include "connection.h"
|
||||
#include "function_entry_node.h"
|
||||
|
||||
struct AstNode
|
||||
{
|
||||
std::shared_ptr<BaseNode> node; // pointeur vers le noeud en cours
|
||||
|
||||
std::vector<std::shared_ptr<AstNode>> inputs;
|
||||
std::shared_ptr<AstNode> condition; // Pour les boucles et les branchements
|
||||
std::vector<std::shared_ptr<AstNode>> body; // Pour les boucles et les branchements
|
||||
std::shared_ptr<AstNode> elseBody; // Pour les branchements
|
||||
|
||||
std::pair<int, int> position;
|
||||
};
|
||||
|
||||
|
||||
|
||||
// AST (Arbre de syntaxe abstraite)
|
||||
struct AST {
|
||||
|
||||
std::map<std::string, int> variables; // Stockage des variables (adresses mémoire)
|
||||
std::string assemblyCode;
|
||||
int labelCounter = 0;
|
||||
std::map<std::string, std::shared_ptr<AstNode>> nodeMap;
|
||||
|
||||
// Fonction pour générer un label unique
|
||||
std::string generateLabel(AST& ast) {
|
||||
return "label" + std::to_string(ast.labelCounter++);
|
||||
}
|
||||
};
|
||||
|
||||
class Compiler
|
||||
{
|
||||
|
||||
public:
|
||||
Compiler() = default;
|
||||
~Compiler() = default;
|
||||
|
||||
|
||||
static std::string FileToConstant(const std::string &FileName, const std::string &extension, IStoryProject &project);
|
||||
|
||||
|
||||
// Fonction pour construire l'AST à partir des nœuds et des connexions
|
||||
void buildAST(std::vector<std::shared_ptr<BaseNode>>& nodes, std::vector<std::shared_ptr<Connection>>& connections) {
|
||||
|
||||
m_ast.nodeMap.clear();
|
||||
|
||||
// Créer une map pour accéder aux nœuds par ID
|
||||
int x = 0, y = 0;
|
||||
for (auto& node : nodes) {
|
||||
auto astNode = std::make_shared<AstNode>();
|
||||
|
||||
astNode->position = {x, y};
|
||||
x += 1; // Espacement horizontal
|
||||
if (x > 20) { // Nouvelle ligne
|
||||
x = 0;
|
||||
y += 1; // Espacement vertical
|
||||
}
|
||||
|
||||
astNode->node = node;
|
||||
m_ast.nodeMap[node->GetId()] = astNode;
|
||||
}
|
||||
|
||||
// Analyser les connexions pour établir les relations entre les nœuds
|
||||
for (const auto& connection : connections) {
|
||||
auto outNode = m_ast.nodeMap[connection->outNodeId];
|
||||
auto inNode = m_ast.nodeMap[connection->inNodeId];
|
||||
|
||||
// // Utiliser les indices des ports pour établir les connexions
|
||||
// if (outNode->inputs.size() <= connection->inPortIndex) {
|
||||
// outNode->inputs.resize(connection->inPortIndex + 1);
|
||||
// }
|
||||
// outNode->inputs[connection->inPortIndex] = inNode;
|
||||
|
||||
// Gérer les sorties en utilisant connection->outPortIndex
|
||||
if (outNode->body.size() <= connection->outPortIndex) {
|
||||
outNode->body.resize(connection->outPortIndex + 1);
|
||||
}
|
||||
outNode->body[connection->outPortIndex] = inNode;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void printAST(const AST& ast, const std::shared_ptr<AstNode>& node, const std::string& prefix = "", bool isLast = true) {
|
||||
// Afficher le nœud actuel
|
||||
std::cout << prefix;
|
||||
std::cout << (isLast ? "└──" : "├──");
|
||||
|
||||
// Afficher les informations du nœud
|
||||
std::cout << "Node: " << node->node->GetTypeName() << " (" << node->node->GetId() << ")" << std::endl;
|
||||
|
||||
// Préparer le préfixe pour les enfants
|
||||
std::string newPrefix = prefix + (isLast ? " " : "│ ");
|
||||
|
||||
// Parcourir les entrées (inputs) du nœud
|
||||
// for (size_t i = 0; i < node->inputs.size(); ++i) {
|
||||
// bool isLastInput = (i == node->inputs.size() - 1);
|
||||
// printAST(ast, node->inputs[i], newPrefix, isLastInput);
|
||||
// }
|
||||
|
||||
// Parcourir les sorties (body) du nœud
|
||||
for (size_t i = 0; i < node->body.size(); ++i) {
|
||||
bool isLastBody = (i == node->body.size() - 1);
|
||||
printAST(ast, node->body[i], newPrefix, isLastBody);
|
||||
}
|
||||
|
||||
// Parcourir le conditionnel (condition) du nœud, s'il existe
|
||||
if (node->condition) {
|
||||
printAST(ast, node->condition, newPrefix, true);
|
||||
}
|
||||
|
||||
// Parcourir le corps du else (elseBody) du nœud, s'il existe
|
||||
if (node->elseBody) {
|
||||
printAST(ast, node->elseBody, newPrefix, true);
|
||||
}
|
||||
}
|
||||
|
||||
void printAST() {
|
||||
// Trouver le nœud de type "function-entry-node"
|
||||
std::shared_ptr<AstNode> entryNode;
|
||||
for (const auto& pair : m_ast.nodeMap) {
|
||||
if (pair.second->node->GetType() == "function-entry-node") {
|
||||
entryNode = pair.second;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!entryNode) {
|
||||
std::cerr << "No function-entry-node found in the AST." << std::endl;
|
||||
return;
|
||||
}
|
||||
|
||||
// Commencer l'affichage de l'arbre à partir du nœud d'entrée
|
||||
printAST(m_ast, entryNode);
|
||||
}
|
||||
|
||||
void generateAssembly() {
|
||||
std::ostringstream assemblyCode;
|
||||
std::map<std::string, std::string> labels;
|
||||
|
||||
// Trouver le nœud de type "function-entry-node"
|
||||
std::shared_ptr<AstNode> entryNode;
|
||||
for (const auto& pair : m_ast.nodeMap) {
|
||||
if (pair.second->node->GetType() == "function-entry-node") {
|
||||
entryNode = pair.second;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!entryNode) {
|
||||
throw std::runtime_error("No function-entry-node found in the AST.");
|
||||
}
|
||||
|
||||
// Fonction récursive pour générer le code assembleur
|
||||
std::function<void(const std::shared_ptr<AstNode>&)> generateCode = [&](const std::shared_ptr<AstNode>& node) {
|
||||
if (node->node->GetType() == "print") {
|
||||
// Générer le code pour un nœud de type "print"
|
||||
assemblyCode << "PRINT ";
|
||||
// Supposons que le nœud print a une entrée qui est une variable
|
||||
if (!node->inputs.empty()) {
|
||||
assemblyCode << node->inputs[0]->node->GetTitle();
|
||||
}
|
||||
assemblyCode << std::endl;
|
||||
} else if (node->node->GetType() == "variable-node") {
|
||||
// Générer le code pour un nœud de type "variable-node"
|
||||
assemblyCode << "LOAD " << node->node->GetTitle() << " INTO REGISTER" << std::endl;
|
||||
} else if (node->node->GetType() == "branch-node") {
|
||||
// Générer le code pour un nœud de type "branch-node"
|
||||
assemblyCode << "BRANCH TO LABEL_" << node->node->GetId() << std::endl;
|
||||
// Générer le code pour les sorties du nœud branch
|
||||
for (const auto& outputNode : node->body) {
|
||||
generateCode(outputNode);
|
||||
}
|
||||
} else if (node->node->GetType() == "function-entry-node") {
|
||||
// Générer le code pour un nœud de type "function-entry-node"
|
||||
assemblyCode << "FUNCTION ENTRY POINT" << std::endl;
|
||||
// Générer le code pour les nœuds connectés à ce nœud
|
||||
for (const auto& inputNode : node->inputs) {
|
||||
generateCode(inputNode);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// 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";
|
||||
|
||||
// Commencer la génération de code à partir du nœud d'entrée
|
||||
generateCode(entryNode);
|
||||
|
||||
// Stocker le code assembleur généré dans l'AST
|
||||
m_ast.assemblyCode = assemblyCode.str();
|
||||
}
|
||||
|
||||
std::string GetCode() const {
|
||||
return m_ast.assemblyCode;
|
||||
}
|
||||
|
||||
private:
|
||||
AST m_ast;
|
||||
|
||||
};
|
||||
|
||||
/*
|
||||
|
||||
class AssemblyGenerator {
|
||||
public:
|
||||
AssemblyGenerator() {
|
||||
Reset();
|
||||
}
|
||||
|
||||
void Reset() {
|
||||
m_assembly.str("");
|
||||
m_labelCounter = 0;
|
||||
m_variableAddresses.clear();
|
||||
m_currentStackOffset = 0;
|
||||
}
|
||||
|
||||
std::string GenerateAssembly(const std::vector<std::shared_ptr<ASTNode>>& roots) {
|
||||
Reset();
|
||||
|
||||
// Generate data section
|
||||
m_assembly << "section .data\n";
|
||||
// Add string constants for print nodes
|
||||
m_assembly << "msg_good db 'Good!',0xa,0\n";
|
||||
m_assembly << "msg_bad db 'Bad :(',0xa,0\n";
|
||||
|
||||
// Generate code section
|
||||
m_assembly << "\nsection .text\n";
|
||||
m_assembly << "global _start\n\n";
|
||||
m_assembly << "_start:\n";
|
||||
|
||||
// Generate entry point code
|
||||
for (const auto& root : roots) {
|
||||
GenerateNodeCode(root);
|
||||
}
|
||||
|
||||
// Add exit syscall
|
||||
m_assembly << " mov eax, 1\n"; // exit syscall
|
||||
m_assembly << " mov ebx, 0\n"; // return 0
|
||||
m_assembly << " int 0x80\n";
|
||||
|
||||
return m_assembly.str();
|
||||
}
|
||||
|
||||
private:
|
||||
std::stringstream m_assembly;
|
||||
int m_labelCounter;
|
||||
std::unordered_map<std::string, int> m_variableAddresses;
|
||||
int m_currentStackOffset;
|
||||
|
||||
std::string GenerateUniqueLabel(const std::string& prefix) {
|
||||
return prefix + "_" + std::to_string(m_labelCounter++);
|
||||
}
|
||||
|
||||
void GenerateNodeCode(std::shared_ptr<ASTNode> node) {
|
||||
if (!node) return;
|
||||
|
||||
if (node->IsType<FunctionEntryNode>()) {
|
||||
// Generate code for function entry
|
||||
m_assembly << " ; Function Entry\n";
|
||||
for (auto& child : node->children) {
|
||||
GenerateNodeCode(child);
|
||||
}
|
||||
}
|
||||
else if (node->IsType<BranchNode>()) {
|
||||
GenerateBranchCode(node);
|
||||
}
|
||||
else if (node->IsType<PrintNode>()) {
|
||||
GeneratePrintCode(node);
|
||||
}
|
||||
else if (node->IsType<VariableNode>()) {
|
||||
GenerateVariableCode(node);
|
||||
}
|
||||
else if (node->IsType<OperatorNode>()) {
|
||||
GenerateOperatorCode(node);
|
||||
}
|
||||
}
|
||||
|
||||
void GenerateBranchCode(std::shared_ptr<ASTNode> node) {
|
||||
std::string labelTrue = GenerateUniqueLabel("true");
|
||||
std::string labelFalse = GenerateUniqueLabel("false");
|
||||
std::string labelEnd = GenerateUniqueLabel("end");
|
||||
|
||||
m_assembly << " ; Branch condition evaluation\n";
|
||||
|
||||
// Generate condition code
|
||||
if (auto conditionNode = node->GetDataInput(0)) {
|
||||
GenerateNodeCode(conditionNode);
|
||||
}
|
||||
|
||||
m_assembly << " pop eax\n";
|
||||
m_assembly << " cmp eax, 0\n";
|
||||
m_assembly << " je " << labelFalse << "\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";
|
||||
}
|
||||
|
||||
void GeneratePrintCode(std::shared_ptr<ASTNode> node) {
|
||||
auto* printNode = node->GetAs<PrintNode>();
|
||||
if (!printNode) return;
|
||||
|
||||
m_assembly << " ; Print message\n";
|
||||
if (printNode->GetText() == "Good!") {
|
||||
m_assembly << " mov edx, 6\n"; // message length
|
||||
m_assembly << " mov ecx, msg_good\n";
|
||||
} else {
|
||||
m_assembly << " mov edx, 6\n"; // message length
|
||||
m_assembly << " mov ecx, msg_bad\n";
|
||||
}
|
||||
m_assembly << " mov ebx, 1\n"; // file descriptor (stdout)
|
||||
m_assembly << " mov eax, 4\n"; // sys_write
|
||||
m_assembly << " int 0x80\n";
|
||||
}
|
||||
|
||||
void GenerateVariableCode(std::shared_ptr<ASTNode> node) {
|
||||
auto* varNode = node->GetAs<VariableNode>();
|
||||
if (!varNode) return;
|
||||
|
||||
// For this example, we'll just push a value onto the stack
|
||||
std::string varName = varNode->GetVariableName();
|
||||
if (m_variableAddresses.find(varName) == m_variableAddresses.end()) {
|
||||
m_variableAddresses[varName] = m_currentStackOffset;
|
||||
m_currentStackOffset += 4;
|
||||
}
|
||||
|
||||
m_assembly << " ; Load variable " << varName << "\n";
|
||||
m_assembly << " mov eax, [ebp-" << m_variableAddresses[varName] << "]\n";
|
||||
m_assembly << " push eax\n";
|
||||
}
|
||||
|
||||
void GenerateOperatorCode(std::shared_ptr<ASTNode> node) {
|
||||
auto* opNode = node->GetAs<OperatorNode>();
|
||||
if (!opNode) return;
|
||||
|
||||
// Generate code for operands
|
||||
for (const auto& [port, inputNode] : node->dataInputs) {
|
||||
GenerateNodeCode(inputNode);
|
||||
}
|
||||
|
||||
m_assembly << " ; Operator " << static_cast<int>(opNode->GetOperationType()) << "\n";
|
||||
|
||||
switch (opNode->GetOperationType()) {
|
||||
case OperatorNode::ADD:
|
||||
m_assembly << " pop ebx\n";
|
||||
m_assembly << " pop eax\n";
|
||||
m_assembly << " add eax, ebx\n";
|
||||
m_assembly << " push eax\n";
|
||||
break;
|
||||
case OperatorNode::SUBTRACT:
|
||||
m_assembly << " pop ebx\n";
|
||||
m_assembly << " pop eax\n";
|
||||
m_assembly << " sub eax, ebx\n";
|
||||
m_assembly << " push eax\n";
|
||||
break;
|
||||
// Add other operators...
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
*/
|
||||
/*
|
||||
|
||||
// Générer des labels uniques pour les nœuds
|
||||
for (const auto& nodePair : m_ast.nodeMap) {
|
||||
std::string label = m_ast.generateLabel(m_ast);
|
||||
labels[nodePair.first] = label;
|
||||
|
||||
// Générer du code pour chaque type de nœud
|
||||
std::string nodeType = nodePair.second->node->GetTypeName();
|
||||
if (nodeType == "LOOP") {
|
||||
// Générer du code pour une boucle
|
||||
assemblyCode += " ; Loop start\n";
|
||||
// Ajouter des instructions pour la condition de la boucle
|
||||
if (nodePair.second->condition) {
|
||||
assemblyCode += " cmp_eq r0, " + labels[nodePair.second->condition->node->GetId()] + ", 0\n";
|
||||
assemblyCode += " skipz r0\n";
|
||||
assemblyCode += " jump " + labels[nodePair.second->body.front()->node->GetId()] + "\n";
|
||||
}
|
||||
// Ajouter des instructions pour le corps de la boucle
|
||||
for (const auto& bodyNode : nodePair.second->body) {
|
||||
assemblyCode += " ; Loop body\n";
|
||||
// Ajouter des instructions pour chaque nœud du corps
|
||||
}
|
||||
assemblyCode += " jump " + label + "\n"; // Retour au début de la boucle
|
||||
} else if (nodeType == "BRANCH") {
|
||||
// Générer du code pour un branchement
|
||||
assemblyCode += " ; Branch start\n";
|
||||
// Ajouter des instructions pour la condition du branchement
|
||||
if (nodePair.second->condition) {
|
||||
assemblyCode += " cmp_eq r0, " + labels[nodePair.second->condition->node->GetId()] + ", 0\n";
|
||||
assemblyCode += " skipnz r0\n";
|
||||
assemblyCode += " jump " + labels[nodePair.second->body.front()->node->GetId()] + "\n";
|
||||
}
|
||||
// Ajouter des instructions pour le corps du branchement
|
||||
for (const auto& bodyNode : nodePair.second->body) {
|
||||
assemblyCode += " ; Branch body\n";
|
||||
// Ajouter des instructions pour chaque nœud du corps
|
||||
}
|
||||
// Ajouter des instructions pour le corps du else
|
||||
if (nodePair.second->elseBody) {
|
||||
assemblyCode += " ; Else body\n";
|
||||
// Ajouter des instructions pour chaque nœud du corps du else
|
||||
}
|
||||
} else {
|
||||
// Générer du code pour d'autres types de nœuds
|
||||
assemblyCode += " ; Other node type: " + nodeType + "\n";
|
||||
assemblyCode += nodePair.second->node->GenerateAssembly();
|
||||
|
||||
// Ajouter des instructions spécifiques au type de nœud
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
*/
|
||||
|
||||
135
core/story-manager/src/compiler/flow_generator.h
Normal file
135
core/story-manager/src/compiler/flow_generator.h
Normal file
|
|
@ -0,0 +1,135 @@
|
|||
#pragma once
|
||||
|
||||
#include "assembly_generator.h"
|
||||
|
||||
#include <iostream>
|
||||
#include <iomanip>
|
||||
#include <chrono>
|
||||
|
||||
class FlowVisualizer {
|
||||
public:
|
||||
static void PrintHeader(const std::string& user, const std::string& timestamp) {
|
||||
std::cout << "==========================================================\n";
|
||||
std::cout << "AST Flow Visualization\n";
|
||||
std::cout << "User: " << user << "\n";
|
||||
std::cout << "Time: " << timestamp << "\n";
|
||||
std::cout << "==========================================================\n\n";
|
||||
}
|
||||
|
||||
static void PrintNodeExecution(const std::string& nodeName, int depth = 0) {
|
||||
PrintTimestamp();
|
||||
std::cout << std::string(depth * 2, ' ') << "→ Executing: " << nodeName << "\n";
|
||||
}
|
||||
|
||||
static void PrintDataFlow(const std::string& from, const std::string& to,
|
||||
const std::string& value, int depth = 0) {
|
||||
PrintTimestamp();
|
||||
std::cout << std::string(depth * 2, ' ') << " ┌─ Data: " << from
|
||||
<< " → " << to << " (value: " << value << ")\n";
|
||||
}
|
||||
|
||||
static void PrintBranchDecision(bool condition, const std::string& value, int depth = 0) {
|
||||
PrintTimestamp();
|
||||
std::cout << std::string(depth * 2, ' ') << " ├─ Branch: "
|
||||
<< (condition ? "TRUE" : "FALSE") << " (condition: " << value << ")\n";
|
||||
}
|
||||
|
||||
private:
|
||||
static void PrintTimestamp() {
|
||||
auto now = std::chrono::system_clock::now();
|
||||
auto now_c = std::chrono::system_clock::to_time_t(now);
|
||||
auto ms = std::chrono::duration_cast<std::chrono::milliseconds>(
|
||||
now.time_since_epoch()) % 1000;
|
||||
|
||||
std::cout << "[" << std::put_time(std::localtime(&now_c), "%H:%M:%S")
|
||||
<< "." << std::setfill('0') << std::setw(3) << ms.count() << "] ";
|
||||
}
|
||||
};
|
||||
|
||||
class VisualFlowGenerator : public AssemblyGenerator {
|
||||
|
||||
public:
|
||||
VisualFlowGenerator(const GeneratorContext& context)
|
||||
: AssemblyGenerator(context)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
protected:
|
||||
void GenerateNodeCode(std::shared_ptr<ASTNode> node, bool isDataPath = false) override {
|
||||
if (!node) return;
|
||||
|
||||
FlowVisualizer::PrintNodeExecution(node->node->GetTypeName(), m_depth);
|
||||
|
||||
if (node->IsType<FunctionEntryNode>()) {
|
||||
m_depth++;
|
||||
for (auto& child : node->children) {
|
||||
GenerateNodeCode(child);
|
||||
}
|
||||
m_depth--;
|
||||
}
|
||||
else if (node->IsType<BranchNode>()) {
|
||||
m_depth++;
|
||||
// Get condition value
|
||||
auto conditionNode = node->GetDataInput(0);
|
||||
int conditionValue = EvaluateCondition(conditionNode);
|
||||
|
||||
FlowVisualizer::PrintBranchDecision(conditionValue > 7,
|
||||
std::to_string(conditionValue), m_depth);
|
||||
|
||||
// Execute appropriate path
|
||||
if (conditionValue > 7) {
|
||||
GenerateNodeCode(node->GetChild(0)); // True path
|
||||
} else {
|
||||
GenerateNodeCode(node->GetChild(1)); // False path
|
||||
}
|
||||
m_depth--;
|
||||
}
|
||||
else if (node->IsType<PrintNode>()) {
|
||||
auto* printNode = node->GetAs<PrintNode>();
|
||||
FlowVisualizer::PrintNodeExecution("Print: " + printNode->GetText(), m_depth);
|
||||
}
|
||||
else if (node->IsType<OperatorNode>()) {
|
||||
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);
|
||||
m_depth--;
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
};
|
||||
|
|
@ -13,30 +13,50 @@
|
|||
class BaseNode
|
||||
{
|
||||
public:
|
||||
struct Port
|
||||
{
|
||||
enum Type {
|
||||
EXECUTION_PORT,
|
||||
DATA_PORT
|
||||
};
|
||||
|
||||
Port::Type type{EXECUTION_PORT};
|
||||
std::string label;
|
||||
};
|
||||
|
||||
struct NodePosition
|
||||
{
|
||||
float x;
|
||||
float y;
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
|
||||
"abcdefghijklmnopqrstuvwxyz"
|
||||
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||
"0123456789"
|
||||
"!@#$%^&*()_+-=[]{}|;:,.<>?";
|
||||
*/
|
||||
|
||||
enum RandomFlags
|
||||
{
|
||||
CHARSET_ALPHABET_LOWER = 0x1,
|
||||
CHARSET_ALPHABET_UPPER = 0x2,
|
||||
CHARSET_NUMBERS = 0x4,
|
||||
CHARSET_SIGNS = 0x8,
|
||||
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
|
||||
{
|
||||
enum Type {
|
||||
TYPE_STRING,
|
||||
TYPE_INT,
|
||||
};
|
||||
std::string stringVal;
|
||||
int intVal{0};
|
||||
ConstantValue::Type type{TYPE_INT};
|
||||
};
|
||||
|
||||
struct Constant
|
||||
{
|
||||
std::string label;
|
||||
int elementSize; // in bits: 8, 16 or 32
|
||||
std::vector<ConstantValue> values;
|
||||
};
|
||||
|
||||
BaseNode(const std::string &type, const std::string &typeName);
|
||||
virtual ~BaseNode();
|
||||
|
||||
|
|
@ -86,6 +106,21 @@ public:
|
|||
|
||||
static std::string GenerateRandomString(size_t length, uint32_t flags = RandomFlags::ALL_CHARSETS);
|
||||
|
||||
void ClearPorts() {
|
||||
m_inputPorts.clear();
|
||||
m_outputPorts.clear();
|
||||
}
|
||||
|
||||
// Port management
|
||||
void AddInputPort(Port::Type type, const std::string& label) {
|
||||
m_inputPorts.push_back({type, label});
|
||||
}
|
||||
|
||||
void AddOutputPort(Port::Type type, const std::string& label) {
|
||||
m_outputPorts.push_back({type, label});
|
||||
}
|
||||
|
||||
|
||||
private:
|
||||
std::string m_title{"Default title"};
|
||||
std::string m_type;
|
||||
|
|
@ -93,6 +128,9 @@ private:
|
|||
std::string m_uuid;
|
||||
NodePosition m_pos;
|
||||
|
||||
std::vector<Port> m_inputPorts;
|
||||
std::vector<Port> m_outputPorts;
|
||||
|
||||
nlohmann::json m_internal_data{{}};
|
||||
};
|
||||
|
||||
|
|
@ -15,6 +15,7 @@ public:
|
|||
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:
|
||||
};
|
||||
|
|
@ -5,6 +5,13 @@
|
|||
|
||||
struct Connection
|
||||
{
|
||||
|
||||
enum Type
|
||||
{
|
||||
EXECUTION_LINKJ,
|
||||
DATA_LINK
|
||||
};
|
||||
|
||||
Connection()
|
||||
: outPortIndex(0)
|
||||
, inPortIndex(0)
|
||||
|
|
@ -16,6 +23,7 @@ struct Connection
|
|||
|
||||
}
|
||||
|
||||
Connection::Type type{Connection::EXECUTION_LINKJ};
|
||||
std::string outNodeId;
|
||||
unsigned int outPortIndex{0};
|
||||
std::string inNodeId;
|
||||
|
|
@ -6,8 +6,8 @@
|
|||
class FunctionEntryNode : public ExecutionNode
|
||||
{
|
||||
public:
|
||||
FunctionEntryNode(const std::string &type, const std::string &typeName)
|
||||
: ExecutionNode(type, typeName) {}
|
||||
FunctionEntryNode(const std::string &type)
|
||||
: ExecutionNode(type, "Function Entry Node") {}
|
||||
|
||||
void Initialize() override {
|
||||
// Initialisation spécifique pour FunctionEntryNode
|
||||
240
core/story-manager/src/nodes/operator_node.h
Normal file
240
core/story-manager/src/nodes/operator_node.h
Normal file
|
|
@ -0,0 +1,240 @@
|
|||
#pragma once
|
||||
|
||||
#include "base_node.h"
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "base_node.h"
|
||||
#include <sstream>
|
||||
#include <stdexcept>
|
||||
#include <unordered_map>
|
||||
|
||||
class OperatorNode : public BaseNode
|
||||
{
|
||||
public:
|
||||
enum class OperationType {
|
||||
ADD, // Addition (+)
|
||||
SUBTRACT, // Subtraction (-)
|
||||
MULTIPLY, // Multiplication (*)
|
||||
DIVIDE, // Division (/)
|
||||
MODULO, // Modulo (%)
|
||||
AND, // Logical AND (&&)
|
||||
OR, // Logical OR (||)
|
||||
NOT, // Logical NOT (!)
|
||||
XOR, // Logical XOR (^)
|
||||
EQUAL, // Equal to (==)
|
||||
NOT_EQUAL, // Not equal to (!=)
|
||||
GREATER_THAN, // Greater than (>)
|
||||
LESS_THAN, // Less than (<)
|
||||
GREATER_EQUAL, // Greater than or equal (>=)
|
||||
LESS_EQUAL, // Less than or equal (<=)
|
||||
BITWISE_AND, // Bitwise AND (&)
|
||||
BITWISE_OR, // Bitwise OR (|)
|
||||
BITWISE_XOR, // Bitwise XOR (^)
|
||||
BITWISE_NOT, // Bitwise NOT (~)
|
||||
LEFT_SHIFT, // Left shift (<<)
|
||||
RIGHT_SHIFT // Right shift (>>)
|
||||
};
|
||||
|
||||
OperatorNode(const std::string& type = "operator-node",
|
||||
const std::string& typeName = "Operator")
|
||||
: BaseNode(type, typeName)
|
||||
, m_operationType(OperationType::ADD)
|
||||
{
|
||||
Initialize();
|
||||
}
|
||||
|
||||
void Initialize() override {
|
||||
// Define input ports based on operator type
|
||||
UpdatePorts();
|
||||
}
|
||||
|
||||
// Set the operator type and update ports accordingly
|
||||
void SetOperationType(OperationType type) {
|
||||
m_operationType = type;
|
||||
UpdatePorts();
|
||||
}
|
||||
|
||||
OperationType GetOperationType() const {
|
||||
return m_operationType;
|
||||
}
|
||||
|
||||
// Get the symbol representation of the operator
|
||||
std::string GetOperatorSymbol() const {
|
||||
static const std::unordered_map<OperationType, std::string> symbolMap = {
|
||||
{OperationType::ADD, "+"},
|
||||
{OperationType::SUBTRACT, "-"},
|
||||
{OperationType::MULTIPLY, "*"},
|
||||
{OperationType::DIVIDE, "/"},
|
||||
{OperationType::MODULO, "%"},
|
||||
{OperationType::AND, "&&"},
|
||||
{OperationType::OR, "||"},
|
||||
{OperationType::NOT, "!"},
|
||||
{OperationType::XOR, "^"},
|
||||
{OperationType::EQUAL, "=="},
|
||||
{OperationType::NOT_EQUAL, "!="},
|
||||
{OperationType::GREATER_THAN, ">"},
|
||||
{OperationType::LESS_THAN, "<"},
|
||||
{OperationType::GREATER_EQUAL, ">="},
|
||||
{OperationType::LESS_EQUAL, "<="},
|
||||
{OperationType::BITWISE_AND, "&"},
|
||||
{OperationType::BITWISE_OR, "|"},
|
||||
{OperationType::BITWISE_XOR, "^"},
|
||||
{OperationType::BITWISE_NOT, "~"},
|
||||
{OperationType::LEFT_SHIFT, "<<"},
|
||||
{OperationType::RIGHT_SHIFT, ">>"}
|
||||
};
|
||||
|
||||
auto it = symbolMap.find(m_operationType);
|
||||
return it != symbolMap.end() ? it->second : "?";
|
||||
}
|
||||
|
||||
std::string Build(IStoryPage& page, const StoryOptions& options, int nb_out_conns) override {
|
||||
std::stringstream ss;
|
||||
ss << "// Operator: " << GetOperatorSymbol() << "\n";
|
||||
|
||||
// For unary operators
|
||||
if (IsUnaryOperator()) {
|
||||
ss << GetOperatorSymbol() << "operand";
|
||||
}
|
||||
// For binary operators
|
||||
else {
|
||||
ss << "operand1 " << GetOperatorSymbol() << " operand2";
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
bool IsUnaryOperator() const {
|
||||
return m_operationType == OperationType::NOT ||
|
||||
m_operationType == OperationType::BITWISE_NOT;
|
||||
}
|
||||
|
||||
void UpdatePorts() {
|
||||
// Clear existing ports
|
||||
ClearPorts();
|
||||
|
||||
// Add input ports based on operator type
|
||||
if (IsUnaryOperator()) {
|
||||
AddInputPort(Port::DATA_PORT, "in");
|
||||
} else {
|
||||
AddInputPort(Port::DATA_PORT, "in1");
|
||||
AddInputPort(Port::DATA_PORT, "in2");
|
||||
}
|
||||
|
||||
// Add output port
|
||||
AddOutputPort(Port::DATA_PORT, "out");
|
||||
}
|
||||
|
||||
static std::string OperatorTypeToString(OperationType type) {
|
||||
static const std::unordered_map<OperationType, std::string> typeMap = {
|
||||
{OperationType::ADD, "add"},
|
||||
{OperationType::SUBTRACT, "subtract"},
|
||||
{OperationType::MULTIPLY, "multiply"},
|
||||
{OperationType::DIVIDE, "divide"},
|
||||
{OperationType::MODULO, "modulo"},
|
||||
{OperationType::AND, "and"},
|
||||
{OperationType::OR, "or"},
|
||||
{OperationType::NOT, "not"},
|
||||
{OperationType::XOR, "xor"},
|
||||
{OperationType::EQUAL, "equal"},
|
||||
{OperationType::NOT_EQUAL, "not_equal"},
|
||||
{OperationType::GREATER_THAN, "greater_than"},
|
||||
{OperationType::LESS_THAN, "less_than"},
|
||||
{OperationType::GREATER_EQUAL, "greater_equal"},
|
||||
{OperationType::LESS_EQUAL, "less_equal"},
|
||||
{OperationType::BITWISE_AND, "bitwise_and"},
|
||||
{OperationType::BITWISE_OR, "bitwise_or"},
|
||||
{OperationType::BITWISE_XOR, "bitwise_xor"},
|
||||
{OperationType::BITWISE_NOT, "bitwise_not"},
|
||||
{OperationType::LEFT_SHIFT, "left_shift"},
|
||||
{OperationType::RIGHT_SHIFT, "right_shift"}
|
||||
};
|
||||
|
||||
auto it = typeMap.find(type);
|
||||
return it != typeMap.end() ? it->second : "unknown";
|
||||
}
|
||||
|
||||
static OperationType StringToOperationType(const std::string& str) {
|
||||
static const std::unordered_map<std::string, OperationType> typeMap = {
|
||||
{"add", OperationType::ADD},
|
||||
{"subtract", OperationType::SUBTRACT},
|
||||
{"multiply", OperationType::MULTIPLY},
|
||||
{"divide", OperationType::DIVIDE},
|
||||
{"modulo", OperationType::MODULO},
|
||||
{"and", OperationType::AND},
|
||||
{"or", OperationType::OR},
|
||||
{"not", OperationType::NOT},
|
||||
{"xor", OperationType::XOR},
|
||||
{"equal", OperationType::EQUAL},
|
||||
{"not_equal", OperationType::NOT_EQUAL},
|
||||
{"greater_than", OperationType::GREATER_THAN},
|
||||
{"less_than", OperationType::LESS_THAN},
|
||||
{"greater_equal", OperationType::GREATER_EQUAL},
|
||||
{"less_equal", OperationType::LESS_EQUAL},
|
||||
{"bitwise_and", OperationType::BITWISE_AND},
|
||||
{"bitwise_or", OperationType::BITWISE_OR},
|
||||
{"bitwise_xor", OperationType::BITWISE_XOR},
|
||||
{"bitwise_not", OperationType::BITWISE_NOT},
|
||||
{"left_shift", OperationType::LEFT_SHIFT},
|
||||
{"right_shift", OperationType::RIGHT_SHIFT}
|
||||
};
|
||||
|
||||
auto it = typeMap.find(str);
|
||||
return it != typeMap.end() ? it->second : OperationType::ADD;
|
||||
}
|
||||
};
|
||||
|
|
@ -22,6 +22,11 @@ public:
|
|||
void SetText(const std::string &text) {
|
||||
m_text = text;
|
||||
}
|
||||
|
||||
std::string GetText() const {
|
||||
return m_text;
|
||||
}
|
||||
|
||||
private:
|
||||
std::string m_label;
|
||||
std::string m_text; // Text to print
|
||||
103
core/story-manager/src/nodes/variable_node.h
Normal file
103
core/story-manager/src/nodes/variable_node.h
Normal file
|
|
@ -0,0 +1,103 @@
|
|||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <variant>
|
||||
|
||||
#include "i_story_manager.h"
|
||||
#include "base_node.h"
|
||||
#include "i_script_node.h"
|
||||
#include "i_story_project.h"
|
||||
|
||||
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 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("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;
|
||||
|
||||
};
|
||||
|
||||
|
|
@ -1,31 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include "base_node.h"
|
||||
|
||||
class OperatorNode : public BaseNode
|
||||
{
|
||||
public:
|
||||
OperatorNode(const std::string &type, const std::string &typeName)
|
||||
: BaseNode(type, typeName) {}
|
||||
|
||||
void Initialize() override {
|
||||
// Initialisation spécifique pour DataNode
|
||||
}
|
||||
|
||||
std::string Build(IStoryPage &page, const StoryOptions &options, int nb_out_conns) override {
|
||||
// Logique de construction pour DataNode
|
||||
return "DataNode Build";
|
||||
}
|
||||
|
||||
std::string GenerateConstants(IStoryPage &page, IStoryProject &project, int nb_out_conns) override {
|
||||
// Génération des constantes pour DataNode
|
||||
return "DataNode Constants";
|
||||
}
|
||||
|
||||
// Ajoutez des méthodes spécifiques pour gérer les données directement
|
||||
void ProcessData() {
|
||||
// Logique pour traiter les données directement
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
|
@ -1,23 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include "i_story_manager.h"
|
||||
#include "base_node.h"
|
||||
#include "i_script_node.h"
|
||||
#include "i_story_project.h"
|
||||
|
||||
class VariableNode : public BaseNode
|
||||
{
|
||||
public:
|
||||
VariableNode(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();
|
||||
|
||||
private:
|
||||
|
||||
};
|
||||
|
||||
|
|
@ -8,11 +8,13 @@ set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
|||
add_executable(${PROJECT_NAME}
|
||||
main.cpp
|
||||
test_ast.cpp
|
||||
../src/base_node.cpp
|
||||
../src/branch_node.cpp
|
||||
../src/print_node.cpp
|
||||
../src/variable_node.cpp
|
||||
../src/connection.cpp
|
||||
|
||||
|
||||
../src/nodes/base_node.cpp
|
||||
../src/nodes/branch_node.cpp
|
||||
../src/nodes/print_node.cpp
|
||||
../src/nodes/variable_node.cpp
|
||||
../src/nodes/connection.cpp
|
||||
|
||||
../../chip32/chip32_assembler.cpp
|
||||
../../chip32/chip32_vm.c
|
||||
|
|
@ -24,4 +26,6 @@ target_include_directories(${PROJECT_NAME} PRIVATE
|
|||
../interfaces
|
||||
../lib
|
||||
../../../shared
|
||||
../src/nodes
|
||||
../src/compiler
|
||||
)
|
||||
|
|
|
|||
|
|
@ -32,33 +32,124 @@ THE SOFTWARE.
|
|||
#include "branch_node.h"
|
||||
#include "print_node.h"
|
||||
#include "variable_node.h"
|
||||
#include "function_entry_node.h"
|
||||
#include "chip32_machine.h"
|
||||
|
||||
#include <stdarg.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "ast_builder.h"
|
||||
#include "assembly_generator.h"
|
||||
#include "flow_generator.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;
|
||||
|
||||
auto printNode = std::make_shared<PrintNode>("print-node");
|
||||
auto printNodeTrue = std::make_shared<PrintNode>("print-node");
|
||||
printNodeTrue->SetText("Good!");
|
||||
|
||||
printNode->SetText("Hello from OST");
|
||||
auto printNodeFalse = std::make_shared<PrintNode>("print-node");
|
||||
printNodeFalse->SetText("Bad :(");
|
||||
|
||||
// auto branchNode = std::make_shared<BranchNode>("branch-node");
|
||||
auto branchNode = std::make_shared<BranchNode>("branch-node");
|
||||
|
||||
auto functionEntryNode = std::make_shared<FunctionEntryNode>("function-entry-node");
|
||||
|
||||
auto variableNode = std::make_shared<VariableNode>("variable-node");
|
||||
|
||||
|
||||
std::vector<std::shared_ptr<BaseNode>> nodes;
|
||||
|
||||
nodes.push_back(printNode);
|
||||
// nodes.push_back(branchNode);
|
||||
nodes.push_back(functionEntryNode);
|
||||
nodes.push_back(printNodeTrue);
|
||||
nodes.push_back(printNodeFalse);
|
||||
nodes.push_back(branchNode);
|
||||
nodes.push_back(variableNode);
|
||||
|
||||
auto cn1 = std::make_shared<Connection>();
|
||||
auto cn2 = std::make_shared<Connection>();
|
||||
auto cn3 = std::make_shared<Connection>();
|
||||
auto cn4 = std::make_shared<Connection>();
|
||||
|
||||
std::vector<std::shared_ptr<Connection>> connections;
|
||||
|
||||
auto cn1 = std::make_shared<Connection>();
|
||||
connections.push_back(cn1);
|
||||
connections.push_back(cn2);
|
||||
connections.push_back(cn3);
|
||||
connections.push_back(cn4);
|
||||
|
||||
|
||||
// Branch True -> print Ok
|
||||
// False -> print Ko
|
||||
|
||||
// True path
|
||||
cn1->inNodeId = printNodeTrue->GetId();
|
||||
cn1->inPortIndex = 0;
|
||||
cn1->outNodeId = branchNode->GetId();
|
||||
cn1->outPortIndex = 0;
|
||||
|
||||
// False path
|
||||
cn2->inNodeId = printNodeFalse->GetId();
|
||||
cn2->inPortIndex = 0;
|
||||
cn2->outNodeId = branchNode->GetId();
|
||||
cn2->outPortIndex = 1;
|
||||
|
||||
// Function entry -> Branch node
|
||||
cn3->inNodeId = branchNode->GetId();
|
||||
cn3->inPortIndex = 0;
|
||||
cn3->outNodeId = functionEntryNode->GetId();
|
||||
cn3->outPortIndex = 0;
|
||||
|
||||
// Variable branch -> Branch node (condition input)
|
||||
cn4->inNodeId = branchNode->GetId();
|
||||
cn4->inPortIndex = 1;
|
||||
cn4->outNodeId = variableNode->GetId();
|
||||
cn4->outPortIndex = 0;
|
||||
cn4->type = Connection::DATA_LINK;
|
||||
|
||||
|
||||
// // Création des nœuds
|
||||
// std::vector<Node> nodes = {
|
||||
// Node(Node::Type::VARIABLE, "i", "node_i"),
|
||||
|
|
@ -75,20 +166,69 @@ TEST_CASE( "Check various indentations and typos" ) {
|
|||
// Node(Node::Type::LOOP, "node_loop")
|
||||
// };
|
||||
|
||||
// try
|
||||
// {
|
||||
// // Construction de l'AST
|
||||
// compiler.buildAST(nodes, connections);
|
||||
// compiler.printAST();
|
||||
|
||||
// } catch(const std::exception &e)
|
||||
// {
|
||||
// std::cout << e.what() << std::endl;
|
||||
// }
|
||||
|
||||
|
||||
// Construction de l'AST
|
||||
compiler.buildAST(nodes, connections);
|
||||
compiler.displayNodeSchema();
|
||||
// Create generator context with current time and user
|
||||
AssemblyGenerator::GeneratorContext context(
|
||||
"2025-04-08 12:09:01", // Current UTC time
|
||||
"arabine", // Current user
|
||||
true, // Enable debug output
|
||||
true, // Enable optimizations
|
||||
1024 // Stack size
|
||||
);
|
||||
|
||||
compiler.generateAssembly();
|
||||
|
||||
std::cout << compiler.GetCode() << std::endl;
|
||||
// Create generator
|
||||
AssemblyGenerator generator(context);
|
||||
|
||||
|
||||
Chip32::Machine machine;
|
||||
ASTBuilder builder(nodes, connections);
|
||||
auto pathTrees = builder.BuildAST();
|
||||
|
||||
machine.QuickExecute(compiler.GetCode());
|
||||
/*
|
||||
// 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);
|
||||
std::string flow = flowGenerator.GenerateAssembly(pathTrees);
|
||||
|
||||
FlowVisualizer::PrintHeader("arabine", "2025-04-08 12:03:01");
|
||||
std::cout << "\nGenerated flow:\n" << flow << std::endl;
|
||||
|
||||
// Generate assembly
|
||||
std::string assembly = generator.GenerateAssembly(pathTrees);
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// compiler.generateAssembly();
|
||||
|
||||
// std::cout << compiler.GetCode() << std::endl;
|
||||
|
||||
|
||||
// Chip32::Machine machine;
|
||||
|
||||
// machine.QuickExecute(compiler.GetCode());
|
||||
|
||||
// REQUIRE( parseResult == true );
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue