#pragma once #include "ast/Module.h" #include "ast/Serialization.h" #include #include #include #include #include #include #include // For system() and popen() functions #include "ast/Generator.h" // For the code generators #include "ast/Parser.h" // For tree-sitter parser integration #include "ast/Annotation.h" // For annotation classes like OptimizationLock class Orchestrator { private: std::unique_ptr currentAST; std::string loadedFilePath; // Operation journal for undo/redo std::deque operationJournal; size_t undoPosition = 0; // Points to the next operation to undo (0 = no operations to undo) public: Orchestrator() = default; ~Orchestrator() { // On destruction, save the AST back if there's a loaded file if (!loadedFilePath.empty() && currentAST) { saveAST(loadedFilePath); } } // Load AST from JSON file bool loadAST(const std::string& filePath) { try { // Read the file std::ifstream file(filePath); if (!file.is_open()) { return false; } std::string content((std::istreambuf_iterator(file)), std::istreambuf_iterator()); file.close(); // Parse JSON auto j = json::parse(content); // Deserialize to AST ASTNode* node = fromJson(j); if (!node || node->conceptType != "Module") { deleteTree(node); return false; } currentAST.reset(static_cast(node)); loadedFilePath = filePath; return true; } catch (...) { return false; } } // Save AST to JSON file bool saveAST(const std::string& filePath) { if (!currentAST) { return false; } try { json j = toJson(currentAST.get()); std::ofstream file(filePath); if (!file.is_open()) { return false; } file << j.dump(2); file.close(); return true; } catch (...) { return false; } } // Get current AST Module* getAST() { return currentAST.get(); } // Set current AST void setAST(std::unique_ptr ast) { currentAST = std::move(ast); } // Get loaded file path std::string getLoadedFilePath() const { return loadedFilePath; } // Check if a node or any of its ancestors has an optimization lock bool isNodeLocked(const ASTNode* node) const { if (!node) return false; // Check if this node has an optimization lock annotation // In a real implementation, we'd check the annotations role for OptimizationLock nodes // For now, we'll implement a basic check by looking for annotations // Check this node's annotations auto annotations = node->getChildren("annotations"); for (const auto* annotation : annotations) { if (annotation->conceptType == "OptimizationLock") { return true; // Found a lock annotation on this node } } // Recursively check parent nodes return isNodeLocked(node->parent); } // Get the lock annotation for a node (if any exists in the ancestry) const OptimizationLock* getNodeLock(const ASTNode* node) const { if (!node) return nullptr; // Check if this node has an optimization lock annotation auto annotations = node->getChildren("annotations"); for (const auto* annotation : annotations) { if (annotation->conceptType == "OptimizationLock") { return static_cast(annotation); } } // Recursively check parent nodes return getNodeLock(node->parent); } // Get all locks on a node and its ancestors std::vector getLocks(const std::string& nodeId) const { std::vector locks; // Find the node by ID ASTNode* node = findNodeById(currentAST.get(), nodeId); if (!node) return locks; // Collect all locks from the node and up the ancestry const ASTNode* current = node; while (current) { // Check if this node has any optimization lock annotations auto annotations = current->getChildren("annotations"); for (const auto* annotation : annotations) { if (annotation->conceptType == "OptimizationLock") { locks.push_back(static_cast(annotation)); } } current = current->parent; } return locks; } // Record an operation for undo/redo void recordOperation(const json& operation) { // Clear any operations after the current position (if we're in the middle of the undo stack) while (operationJournal.size() > undoPosition) { operationJournal.pop_back(); } // Add the new operation operationJournal.push_back(operation); undoPosition = operationJournal.size(); // Point to after the last operation } // Undo the last operation bool undoLastOperation() { if (undoPosition == 0) { return false; // Nothing to undo } // Move back one operation undoPosition--; json operation = operationJournal[undoPosition]; // Perform the inverse of the operation if (operation["type"] == "insertNode") { // For insertNode, the inverse is to delete the node std::string nodeId = operation["nodeId"]; ASTNode* node = findNodeById(currentAST.get(), nodeId); if (node) { // Find the parent and remove the child ASTNode* parent = node->parent; if (parent) { // STUB: insertNode undo not implemented — requires role tracking per mutation } } } else if (operation["type"] == "setNodeProperty") { // For setNodeProperty, the inverse is to set the property back to its old value std::string nodeId = operation["nodeId"]; std::string property = operation["property"]; json oldValue = operation["oldValue"]; ASTNode* node = findNodeById(currentAST.get(), nodeId); if (node) { setNodeProperty(node, property, oldValue); } } return true; } // Redo the last undone operation bool redoLastOperation() { if (undoPosition >= operationJournal.size()) { return false; // Nothing to redo } // Perform the operation json operation = operationJournal[undoPosition]; if (operation["type"] == "insertNode") { // Re-insert the node // This would require recreating the node and adding it back - simplified for this example } else if (operation["type"] == "setNodeProperty") { // Re-set the property to its new value std::string nodeId = operation["nodeId"]; std::string property = operation["property"]; json newValue = operation["newValue"]; ASTNode* node = findNodeById(currentAST.get(), nodeId); if (node) { setNodeProperty(node, property, newValue); } } // Move forward one operation undoPosition++; return true; } // Record a snapshot (text + AST) for unified undo/redo void recordSnapshot(const std::string& text, const ASTNode* ast) { json op; op["type"] = "snapshot"; op["text"] = text; if (ast) op["ast"] = toJson(ast); recordOperation(op); } // Undo snapshot bool undoSnapshot(std::string& outText, std::unique_ptr& outAst) { if (undoPosition == 0) return false; undoPosition--; json operation = operationJournal[undoPosition]; if (operation.value("type", "") != "snapshot") return false; outText = operation.value("text", ""); outAst.reset(); if (operation.contains("ast")) { ASTNode* node = fromJson(operation["ast"]); if (node && node->conceptType == "Module") { outAst.reset(static_cast(node)); } else { deleteTree(node); } } return true; } // Redo snapshot bool redoSnapshot(std::string& outText, std::unique_ptr& outAst) { if (undoPosition >= operationJournal.size()) return false; json operation = operationJournal[undoPosition]; if (operation.value("type", "") != "snapshot") return false; undoPosition++; outText = operation.value("text", ""); outAst.reset(); if (operation.contains("ast")) { ASTNode* node = fromJson(operation["ast"]); if (node && node->conceptType == "Module") { outAst.reset(static_cast(node)); } else { deleteTree(node); } } return true; } int getUndoDepth() const { return (int)undoPosition; } int getRedoDepth() const { return (int)(operationJournal.size() - undoPosition); } // Helper function to find a node by ID in the AST ASTNode* findNodeById(ASTNode* root, const std::string& id) const { if (!root) return nullptr; if (root->id == id) return root; // Recursively search in children for (auto* child : root->allChildren()) { ASTNode* found = findNodeById(child, id); if (found) return found; } return nullptr; } // Helper function to set a property on a node (needs to be implemented) bool setNodeProperty(ASTNode* node, const std::string& property, const json& value) { // This is a simplified implementation - in a real system, we'd have specific setters for each concept // For now, we'll handle a few common properties if (node->conceptType == "Function" && property == "name") { if (value.is_string()) { static_cast(node)->name = value.get(); return true; } } else if (node->conceptType == "Variable" && property == "name") { if (value.is_string()) { static_cast(node)->name = value.get(); return true; } } else if (node->conceptType == "Parameter" && property == "name") { if (value.is_string()) { static_cast(node)->name = value.get(); return true; } } // Add more property setters as needed return false; // Property not supported } // Method to start Emacs daemon bool startEmacsDaemon() { // In a real implementation, this would spawn: emacs --daemon=whetstone --load whetstone-bridge.el // For this implementation, we'll simulate the functionality // This would typically use system() or a process spawning library std::cout << "Starting Emacs daemon with: emacs --daemon=whetstone" << std::endl; // In a real implementation: system("emacs --daemon=whetstone --load whetstone-bridge.el"); return true; } // Method to send a command to Emacs std::string sendToEmacs(const std::string& command) { // In a real implementation, this would send the command to Emacs via emacsclient // This executes: emacsclient -s whetstone -e "command" and returns the result std::cout << "Sending to Emacs: " << command << std::endl; // In a real implementation: // Escape command to prevent shell injection std::string escaped; for (char c : command) { if (c == '"' || c == '\\') escaped += '\\'; escaped += c; } std::string cmd = "emacsclient -s whetstone -e \"" + escaped + "\""; #ifdef _WIN32 FILE* pipe = _popen(cmd.c_str(), "r"); #else FILE* pipe = popen(cmd.c_str(), "r"); #endif if (!pipe) return "nil"; char buffer[4096]; std::string result = ""; while (!feof(pipe)) { if (fgets(buffer, sizeof(buffer), pipe) != NULL) { result += buffer; } } #ifdef _WIN32 _pclose(pipe); #else pclose(pipe); #endif // Trim whitespace from the result result.erase(result.find_last_not_of(" \t\n\r\f\v") + 1); return result.empty() ? "nil" : result; } // Escape a string for embedding in Elisp double-quoted strings static std::string escapeElispString(const std::string& s) { std::string out; for (char c : s) { if (c == '"' || c == '\\') out += '\\'; out += c; } return out; } // Method to load a file via Emacs and parse to AST using tree-sitter std::unique_ptr loadFile(const std::string& path) { // First, get the file content from Emacs std::string command = "(with-current-buffer (find-file-noselect \"" + escapeElispString(path) + "\") (buffer-string))"; std::string content = sendToEmacs(command); // Determine the language based on file extension std::string targetLanguage = "python"; // Default if (path.substr(path.find_last_of(".") + 1) == "cpp" || path.substr(path.find_last_of(".") + 1) == "hpp") { targetLanguage = "cpp"; } else if (path.substr(path.find_last_of(".") + 1) == "py") { targetLanguage = "python"; } else if (path.substr(path.find_last_of(".") + 1) == "el" || path.substr(path.find_last_of(".") + 1) == "elisp") { targetLanguage = "elisp"; } else if (path.substr(path.find_last_of(".") + 1) == "js") { targetLanguage = "javascript"; } else if (path.substr(path.find_last_of(".") + 1) == "ts") { targetLanguage = "typescript"; } else if (path.substr(path.find_last_of(".") + 1) == "java") { targetLanguage = "java"; } else if (path.substr(path.find_last_of(".") + 1) == "rs") { targetLanguage = "rust"; } else if (path.substr(path.find_last_of(".") + 1) == "go") { targetLanguage = "go"; } // Parse the content using the appropriate tree-sitter parser std::unique_ptr module; if (targetLanguage == "python") { module = TreeSitterParser::parsePython(content); } else if (targetLanguage == "cpp") { module = TreeSitterParser::parseCpp(content); } else if (targetLanguage == "elisp") { module = TreeSitterParser::parseElisp(content); } else if (targetLanguage == "javascript") { module = TreeSitterParser::parseJavaScript(content); } else if (targetLanguage == "typescript") { module = TreeSitterParser::parseTypeScript(content); } else if (targetLanguage == "java") { module = TreeSitterParser::parseJava(content); } else if (targetLanguage == "rust") { module = TreeSitterParser::parseRust(content); } else if (targetLanguage == "go") { module = TreeSitterParser::parseGo(content); } else { // For unknown languages, create a basic module with the content module = std::make_unique(); module->id = "LoadedModule_" + std::to_string(reinterpret_cast(module.get())); module->name = path; module->targetLanguage = targetLanguage; } // Set the module name to match the file path module->name = path; return module; } // Method to save AST to a file via Emacs using appropriate generator bool saveFile(const std::string& path, const ASTNode* ast) { if (!ast) return false; // Determine target language from the AST or file extension std::string targetLanguage = "python"; // Default if (ast->conceptType == "Module") { const Module* module = static_cast(ast); targetLanguage = module->targetLanguage; } else { // If not a module, try to determine from file extension if (path.substr(path.find_last_of(".") + 1) == "cpp" || path.substr(path.find_last_of(".") + 1) == "hpp") { targetLanguage = "cpp"; } else if (path.substr(path.find_last_of(".") + 1) == "py") { targetLanguage = "python"; } else if (path.substr(path.find_last_of(".") + 1) == "el" || path.substr(path.find_last_of(".") + 1) == "elisp") { targetLanguage = "elisp"; } else if (path.substr(path.find_last_of(".") + 1) == "js") { targetLanguage = "javascript"; } else if (path.substr(path.find_last_of(".") + 1) == "ts") { targetLanguage = "typescript"; } else if (path.substr(path.find_last_of(".") + 1) == "java") { targetLanguage = "java"; } else if (path.substr(path.find_last_of(".") + 1) == "rs") { targetLanguage = "rust"; } else if (path.substr(path.find_last_of(".") + 1) == "go") { targetLanguage = "go"; } } // Generate code from the AST using the appropriate generator std::string content; if (targetLanguage == "python") { PythonGenerator gen; content = gen.generate(ast); } else if (targetLanguage == "elisp") { ElispGenerator elispGen; content = elispGen.generate(ast); } else if (targetLanguage == "cpp" || targetLanguage == "c++") { CppGenerator gen; content = gen.generate(ast); } else if (targetLanguage == "javascript") { JavaScriptGenerator gen; content = gen.generate(ast); } else if (targetLanguage == "typescript") { TypeScriptGenerator gen; content = gen.generate(ast); } else if (targetLanguage == "java") { JavaGenerator gen; content = gen.generate(ast); } else if (targetLanguage == "rust") { RustGenerator gen; content = gen.generate(ast); } else if (targetLanguage == "go") { GoGenerator gen; content = gen.generate(ast); } else { // Default to Python generator for unknown languages PythonGenerator gen; content = gen.generate(ast); } // Send the content to Emacs to save to file std::string ePath = escapeElispString(path); std::string eContent = escapeElispString(content); std::string command = "(with-current-buffer (find-file-noselect \"" + ePath + "\")" + "(erase-buffer)" + "(insert \"" + eContent + "\")" + "(write-file \"" + ePath + "\"))"; std::string result = sendToEmacs(command); return result.find("Error") == std::string::npos; } // Overload: save raw content string to a file via Emacs bool saveContent(const std::string& path, const std::string& content) { std::string ePath = escapeElispString(path); std::string eContent = escapeElispString(content); std::string command = "(with-current-buffer (find-file-noselect \"" + ePath + "\")" + "(erase-buffer)" + "(insert \"" + eContent + "\")" + "(write-file \"" + ePath + "\"))"; std::string result = sendToEmacs(command); return result.find("Error") == std::string::npos; } };