Signed-off-by: NotAShelf <raf@notashelf.dev> Change-Id: Ib9abc9d2dcd036d3680c5aa3dc919bfa6a6a6964
114 lines
3 KiB
C++
114 lines
3 KiB
C++
#include "resolver.h"
|
|
#include "parser.h"
|
|
#include <filesystem>
|
|
#include <fstream>
|
|
#include <iostream>
|
|
#include <regex>
|
|
#include <sstream>
|
|
|
|
namespace nix_irc {
|
|
|
|
namespace fs = std::filesystem;
|
|
|
|
struct Resolver::Impl {
|
|
ResolverConfig config;
|
|
std::vector<std::pair<std::string, std::string>> resolved_imports;
|
|
std::unordered_set<std::string> visited;
|
|
Parser parser;
|
|
|
|
Impl(const ResolverConfig& cfg) : config(cfg) {}
|
|
|
|
std::string resolve_path(const std::string& path, const std::string& from_file) {
|
|
fs::path p(path);
|
|
|
|
if (p.is_absolute()) {
|
|
if (fs::exists(p))
|
|
return path;
|
|
return "";
|
|
}
|
|
|
|
fs::path from_dir = fs::path(from_file).parent_path();
|
|
fs::path candidate = from_dir / p;
|
|
if (fs::exists(candidate))
|
|
return candidate.string();
|
|
|
|
for (const auto& search : config.search_paths) {
|
|
candidate = fs::path(search) / p;
|
|
if (fs::exists(candidate))
|
|
return candidate.string();
|
|
}
|
|
|
|
return "";
|
|
}
|
|
|
|
ImportResult do_resolve(const std::string& path, const std::string& from_file) {
|
|
std::string resolved = resolve_path(path, from_file);
|
|
|
|
if (resolved.empty()) {
|
|
return {false, "", "Cannot find file: " + path, nullptr};
|
|
}
|
|
|
|
if (visited.count(resolved)) {
|
|
return {true, resolved, "", nullptr};
|
|
}
|
|
visited.insert(resolved);
|
|
|
|
try {
|
|
auto ast = parser.parse_file(resolved);
|
|
return {true, resolved, "", ast};
|
|
} catch (const std::exception& e) {
|
|
return {false, "", e.what(), nullptr};
|
|
}
|
|
}
|
|
};
|
|
|
|
Resolver::Resolver(const ResolverConfig& config) : pImpl(std::make_unique<Impl>(config)) {}
|
|
Resolver::~Resolver() = default;
|
|
|
|
void Resolver::add_search_path(const std::string& path) {
|
|
pImpl->config.search_paths.push_back(path);
|
|
}
|
|
|
|
void Resolver::set_search_paths(const std::vector<std::string>& paths) {
|
|
pImpl->config.search_paths = paths;
|
|
}
|
|
|
|
ImportResult Resolver::resolve_import(const std::string& path, const std::string& from_file) {
|
|
auto result = pImpl->do_resolve(path, from_file);
|
|
if (result.success && result.ast) {
|
|
pImpl->resolved_imports.push_back({path, result.path});
|
|
}
|
|
return result;
|
|
}
|
|
|
|
ImportResult Resolver::resolve_import(const Node& import_node, const std::string& from_file) {
|
|
const ConstPathNode* path_node = import_node.get_if<ConstPathNode>();
|
|
if (!path_node) {
|
|
return {false, "", "Dynamic import not supported", nullptr};
|
|
}
|
|
return resolve_import(path_node->value, from_file);
|
|
}
|
|
|
|
std::vector<std::string> Resolver::get_resolved_files() const {
|
|
std::vector<std::string> files;
|
|
for (const auto& [orig, resolved] : pImpl->resolved_imports) {
|
|
(void) orig;
|
|
files.push_back(resolved);
|
|
}
|
|
return files;
|
|
}
|
|
|
|
std::vector<std::pair<std::string, std::string>> Resolver::get_imports() const {
|
|
return pImpl->resolved_imports;
|
|
}
|
|
|
|
bool is_static_import(const Node& node) {
|
|
return node.holds<ConstPathNode>();
|
|
}
|
|
|
|
std::string normalize_path(const std::string& path) {
|
|
fs::path p(path);
|
|
return fs::absolute(p).string();
|
|
}
|
|
|
|
} // namespace nix_irc
|