(wip) compiler with ast

This commit is contained in:
Anthony Rabine 2025-03-09 14:36:28 +01:00
parent bbf149dedd
commit d3a8b10120
10 changed files with 18631 additions and 26 deletions

27
.gitignore vendored
View file

@ -5,8 +5,6 @@ www-docs/node_modules/
*.lock *.lock
build-story-editor-Desktop_Qt_6_4_1_GCC_64bit-Debug/
docs/resources/ docs/resources/
docs/node_modules/ docs/node_modules/
@ -21,23 +19,11 @@ hardware/kicad/ost-pico-addon/fp-info-cache
*.png~ *.png~
build-story-editor-Desktop_Qt_6_5_1_GCC_64bit-Debug/
build-story-editor-Desktop_Qt_6_5_1_MinGW_64_bit-Debug/
build-story-editor-Desktop_Qt_6_5_1_MinGW_64_bit-Release/
build-story-editor-Desktop_Qt_6_5_1_MSVC2019_64bit-Debug/
release/ release/
story-editor/windows-setup/Output/ story-editor/windows-setup/Output/
build-story-editor-Desktop_Qt_6_5_1_GCC_64bit-Release/
build-story-player-Desktop_Qt_6_5_1_GCC_64bit-Debug/
build-story-player-Desktop_Qt_GCC_64bit-Debug/
*.user *.user
@ -47,20 +33,12 @@ software/.vscode/.cortex-debug.registers.state.json
story-player/build/ story-player/build/
story-editor/tools/build-audio-System_Qt5_15_8-Debug/
software/.vscode/.cortex-debug.peripherals.state.json software/.vscode/.cortex-debug.peripherals.state.json
story-editor/build story-editor/build
build-story-editor-v2-System_Qt5_15_8-Debug/
story-editor-v2/src/CMakeSettings.json
story-editor-v2/imgui.ini
story-editor-v2/src/.vscode/
build-story-editor-v2-Desktop-Debug/
build-story-editor-Desktop-Debug/ build-story-editor-Desktop-Debug/
@ -75,7 +53,6 @@ software/.cache/
story-editor/.idea/ story-editor/.idea/
build-story-editor-Desktop_Qt_GCC_64bit-Debug/
firmware/build/ firmware/build/
@ -107,3 +84,5 @@ story-editor/cmake-build-debug/
story-editor/.flatpak-builder story-editor/.flatpak-builder
story-editor/flatpak_build/ story-editor/flatpak_build/
story-editor/AppDir/ story-editor/AppDir/
core/story-manager/tests/build

View file

@ -1,10 +1,11 @@
{ {
"cmake.sourceDirectory": [ "cmake.sourceDirectory": [
"${workspaceFolder}/core/chip32/tests",
"${workspaceFolder}/core/story-manager/tests",
"${workspaceFolder}/story-editor", "${workspaceFolder}/story-editor",
"${workspaceFolder}/story-player-raylib", "${workspaceFolder}/story-player-raylib",
"${workspaceFolder}/software", "${workspaceFolder}/software"
"${workspaceFolder}/core/chip32/tests"
], ],
"files.associations": { "files.associations": {
"*.css": "tailwindcss", "*.css": "tailwindcss",

View file

@ -0,0 +1,306 @@
#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;
}

View file

@ -2,6 +2,38 @@
#include "sys_lib.h" #include "sys_lib.h"
#include "i_story_project.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 class Compiler
{ {
@ -10,7 +42,110 @@ public:
Compiler() = default; Compiler() = default;
~Compiler() = default; ~Compiler() = default;
static std::string FileToConstant(const std::string &FileName, const std::string &extension, IStoryProject &project); 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 = {0, 0};
x += 10; // Espacement horizontal
if (x > 50) { // Nouvelle ligne
x = 0;
y += 5; // 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) {
if (node.type == Node::Type::LOOP || node.type == Node::Type::BRANCH){
for (auto& connection : connections) {
if (connection.inNodeId == node.id && connection.inPortIndex == 0) { // Condition
node.condition = nodeMap[connection.outNodeId];
}
if (connection.inNodeId == node.id && connection.inPortIndex == 1) { // Body
node.body.push_back(nodeMap[connection.outNodeId]);
}
if (node.type == Node::Type::BRANCH && connection.inNodeId == node.id && connection.inPortIndex == 2) { // ElseBody
if(node.elseBody == nullptr) {
node.elseBody = new Node(Node::Type::VARIABLE, "dummy"); // Créer un noeud dummy juste pour l'elsebody.
node.elseBody->body.push_back(nodeMap[connection.outNodeId]);
} else {
node.elseBody->body.push_back(nodeMap[connection.outNodeId]);
}
}
}
}
}
*/
}
// Fonction pour afficher le schéma des nœuds
void displayNodeSchema() {
// Afficher les nœuds
for (auto& astNode : m_ast.nodeMap) {
auto pos = astNode.second->position;
std::cout << "\033[" << pos.second + 1 << ";" << pos.first + 1 << "H"; // Déplacer le curseur
std::cout << "[" << astNode.second->node->GetTypeName() << "]";
}
/*
// Afficher les connexions
for (auto& connection : connections) {
auto outPos = nodePositions[connection.outNodeId];
auto inPos = nodePositions[connection.inNodeId];
// Dessiner une ligne entre les nœuds (simplifié)
int startX = outPos.first + 10;
int startY = outPos.second + 1;
int endX = inPos.first;
int endY = inPos.second + 1;
if (startY == endY) { // Ligne horizontale
for (int i = startX; i < endX; ++i) {
std::cout << "\033[" << startY << ";" << i + 1 << "H-";
}
} else { // Ligne verticale + horizontale
for (int i = startX; i < startX + (endX - startX) / 2; ++i) {
std::cout << "\033[" << startY << ";" << i + 1 << "H-";
}
for (int i = std::min(startY, endY); i < std::max(startY, endY); ++i) {
std::cout << "\033[" << i + 1 << ";" << startX + (endX - startX) / 2 + 1 << "H|";
}
for (int i = startX + (endX - startX) / 2 + 1; i < endX; ++i) {
std::cout << "\033[" << endY << ";" << i + 1 << "H-";
}
}
std::cout << "\033[" << endY << ";" << endX << "H>";
}
std::cout << "\033[" << 100 << ";" << 1 << "H"; // Déplacer le curseur en bas
*/
}
private:
AST m_ast;
}; };

View file

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

View file

@ -0,0 +1,21 @@
#pragma once
#include <string>
#include "i_story_manager.h"
#include "base_node.h"
#include "i_script_node.h"
#include "i_story_project.h"
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;
private:
};

View file

@ -0,0 +1,16 @@
cmake_minimum_required(VERSION 3.5)
project(compiler_test LANGUAGES CXX C)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
add_executable(${PROJECT_NAME}
main.cpp
test_ast.cpp
../../chip32/chip32_assembler.cpp
../../chip32/chip32_vm.c
)
target_include_directories(${PROJECT_NAME} PRIVATE ../../chip32)

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,46 @@
/*
The MIT License
Copyright (c) 2022 Anthony Rabine
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
#include <iostream>
#define CATCH_CONFIG_MAIN
#include "catch.hpp"
void hexdump(void *ptr, int buflen) {
unsigned char *buf = (unsigned char*)ptr;
int i, j;
for (i=0; i<buflen; i+=16) {
printf("%06x: ", i);
for (j=0; j<16; j++)
if (i+j < buflen)
printf("%02x ", buf[i+j]);
else
printf(" ");
printf(" ");
for (j=0; j<16; j++)
if (i+j < buflen)
printf("%c", isprint(buf[i+j]) ? buf[i+j] : '.');
printf("\n");
}
}

View file

@ -0,0 +1,82 @@
/*
The MIT License
Copyright (c) 2022 Anthony Rabine
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
#include <iostream>
#include <thread>
#include "catch.hpp"
#include "chip32_assembler.h"
#include "chip32_macros.h"
#include "compiler.h"
#include "branch_node.h"
#include "print_node.h"
#include "variable_node.h"
#include <stdarg.h>
#include <string.h>
TEST_CASE( "Check various indentations and typos" ) {
Compiler compiler;
auto printNode = std::make_shared<PrintNode>();
auto branchNode = std::make_shared<BranchNode>();
std::vector<std::shared_ptr<BaseNode>> nodes;
nodes.push_back(printNode);
nodes.push_back(branchNode);
std::vector<std::shared_ptr<Connection>> connections;
auto cn1 = std::make_shared<Connection>();
// // 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")
// };
// Construction de l'AST
AST ast = buildAST(nodes, connections);
REQUIRE( parseResult == true );
}