Document language for dates; @eval pathname resolution and :cwd (from dev bc7cd62b6f68)
@date/@datetime gain :lang (German: "16. Juni 1910") over a document-wide Language state variable, and :number for the numeric form (en 6/16/1910, de 16.06.1910 per DIN 5008). @eval finds Python modules next to the file that names them regardless of the cwd, and the new :cwd option runs an eval in a chosen working directory (@source_file uses it to resolve against the document). Two new test suites ship in tst/. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
65
mac/eval.cpp
65
mac/eval.cpp
@@ -7,6 +7,7 @@
|
||||
#include "katom.h"
|
||||
#include "file.h"
|
||||
#include <algorithm>
|
||||
#include <optional>
|
||||
#include <unistd.h>
|
||||
|
||||
std::string shell(State state, std::string command, Locator loc)
|
||||
@@ -114,12 +115,64 @@ void check_cpp_arguments(katom_list args, Locator loc)
|
||||
}
|
||||
|
||||
|
||||
// Save the process working directory, change to DIR, and restore on
|
||||
// destruction (exception-safe), so an @eval's :cwd cannot leak into the
|
||||
// rest of the run. NOTE: the cwd is process-global state; if input files
|
||||
// are ever processed in parallel, this needs rethinking.
|
||||
class Cwd_guard
|
||||
{
|
||||
public:
|
||||
explicit Cwd_guard(const std::string& dir)
|
||||
: m_saved(fs::current_path())
|
||||
{
|
||||
fs::current_path(dir);
|
||||
}
|
||||
~Cwd_guard()
|
||||
{
|
||||
std::error_code ec;
|
||||
fs::current_path(m_saved, ec); // never throw from a destructor
|
||||
}
|
||||
Cwd_guard(const Cwd_guard&) = delete;
|
||||
Cwd_guard& operator=(const Cwd_guard&) = delete;
|
||||
private:
|
||||
fs::path m_saved;
|
||||
};
|
||||
|
||||
|
||||
std::string Eval::eval_command(katom_iter begin, katom_iter end)
|
||||
{
|
||||
(void)K::log(3, *begin, *(end - 1));
|
||||
//msg() << "in Eval::eval:\n" << ktype << kall << kindex << std::pair(begin, end) << "\n";
|
||||
katom_iter first = after_whitespace(begin + 1);
|
||||
std::string eval_result = "[unevaluated]";
|
||||
|
||||
// :cwd DIR — run the eval (any mode) with DIR as the working directory,
|
||||
// restoring the process cwd afterwards. The default is the directory
|
||||
// ktext was started in (unchanged behavior). DIR may hold state
|
||||
// substitutions (:cwd *K_input_dir* is the document's directory) and,
|
||||
// per the filenames-with-spaces convention, whitespace-separated tokens
|
||||
// are joined until they name an existing directory.
|
||||
std::optional<Cwd_guard> cwd_guard {};
|
||||
if (first->m_text == ":cwd") {
|
||||
katom_iter tok = after_whitespace(first + 1);
|
||||
std::string dir {};
|
||||
katom_iter cursor = tok;
|
||||
katom_iter resume = tok;
|
||||
while (cursor < end - 1 && !cursor->m_text.starts_with(":")) {
|
||||
dir = m_machine.m_state.subst(as_string(tok, cursor + 1, true));
|
||||
resume = after_whitespace(cursor + 1);
|
||||
if (fs::is_directory(dir)) break;
|
||||
cursor = resume;
|
||||
}
|
||||
if (dir.empty() || !fs::is_directory(dir)) {
|
||||
throw Argument_error(
|
||||
"The :cwd directory does not exist: \"" + dir + "\"",
|
||||
begin->m_loc, false);
|
||||
}
|
||||
cwd_guard.emplace(dir);
|
||||
first = resume;
|
||||
}
|
||||
|
||||
std::string first_word = first->m_text;
|
||||
int offset = first_word[0] == ':' ? 1 : 0;
|
||||
std::string command = as_string(first + offset, end - 1, true);
|
||||
@@ -145,6 +198,18 @@ std::string Eval::eval_command(katom_iter begin, katom_iter end)
|
||||
lib_text = string_replace(lib_text, "*KLAMMERTEXT_HOME*", khome);
|
||||
}
|
||||
fs::path libpath(lib_text + ".so");
|
||||
// A relative library name not found from the cwd is searched in the
|
||||
// directories of the files the Machine has read (same rule as the
|
||||
// Python module path: the library lives next to the file using it).
|
||||
if (libpath.is_relative() && !fs::exists(fs::absolute(libpath))) {
|
||||
for (const auto& dir : m_machine.m_state.m_search_dirs) {
|
||||
fs::path candidate = fs::path(dir) / libpath;
|
||||
if (fs::exists(candidate)) {
|
||||
libpath = candidate;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
libpath = fs::absolute(libpath);
|
||||
std::string funcname = args.size() == 4 ? libpath.stem().string() : args[3].m_text;
|
||||
Eval_cpp E_cpp(m_machine, begin->m_loc);
|
||||
|
||||
Reference in New Issue
Block a user