/* ** Copyright (C) 2025 Dirk-Jan C. Binnema ** ** This program is free software; you can redistribute it and/or modify it ** under the terms of the GNU General Public License as published by the ** Free Software Foundation; either version 3, or (at your option) any ** later version. ** ** This program is distributed in the hope that it will be useful, ** but WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ** GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with this program; if not, write to the Free Software Foundation, ** Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. ** */ #ifndef MU_SCM_HH #define MU_SCM_HH #include #include #include #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wredundant-decls" #include #pragma GCC diagnostic pop #include "utils/mu-result.hh" #include "mu/mu-options.hh" #include "mu-store.hh" /** * Namespace for the Scm (Guile) subsystem * */ namespace Mu::Scm { /** * Configuration object * */ struct Config { const Mu::Store& store; const Options& options; }; /** * Start a guile shell * * Initialize the Scm sub-system, then start a shell or run a script, * based on the configuration. * * @param conf a Config object * * @return Ok() or some error */ Result run(const Config& conf); /** * Helpers * * @{*/ // https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2022/p2593r0.html template struct always_false : std::false_type {}; template constexpr bool is_char_array_v = std::is_array_v && std::is_same_v, char>; /** * Make SCM symbol from string-like value * * @param val some value * * @return an SCM symbol */ template SCM make_symbol(const T& val){ using Type = std::remove_const_t; // *not* std::remove_const if constexpr (std::is_same_v || std::is_same_v) return scm_from_utf8_symboln(val.data(), val.size()); else if constexpr (is_char_array_v|| std::is_same_v) return scm_from_utf8_symbol(val); else { static_assert(always_false::value, "source type not supported"); return SCM_UNSPECIFIED; } } /** * Get some C++ value from an SCM object, generically. * * @param ARG some SCM object * * @return C++ value */ template T from_scm(SCM ARG) { using Type = std::remove_const_t; // *not* std::remove_const if constexpr (std::is_same_v) { SCM_ASSERT(scm_string_p(ARG), ARG, SCM_ARG1, __func__); auto str{scm_to_utf8_string(ARG)}; std::string res{str}; ::free(str); return res; } else if constexpr (std::is_same_v) { SCM_ASSERT(scm_char_p(ARG), ARG, SCM_ARG1, __func__); return scm_to_char(ARG); } else if constexpr (std::is_same_v) { SCM_ASSERT(scm_boolean_p(ARG), ARG, SCM_ARG1, __func__); return scm_to_bool(ARG); } else if constexpr (std::is_same_v) { SCM_ASSERT(scm_is_signed_integer(ARG, std::numeric_limits::min(), std::numeric_limits::max()), ARG, SCM_ARG1, __func__); return scm_to_int(ARG); } else if constexpr (std::is_same_v) { SCM_ASSERT(scm_is_unsigned_integer(ARG, std::numeric_limits::min(), std::numeric_limits::max()), ARG, SCM_ARG1, __func__); return scm_to_uint(ARG); } else if constexpr (std::is_same_v) { return ARG; } else { static_assert(always_false::value, "target type not supported"); return {}; } } /** * Like from_SCM, but if ARG is boolean false, return default value. * * @param ARG argument * @param default_value default value * * @return value */ template T from_scm_with_default(SCM ARG, const T default_value) { return (scm_is_bool(ARG) && scm_is_false(ARG)) ? default_value : from_scm(ARG); } /** * Get some SCM from a C++ value, generically. * * @param val some C++ object * * @return an SCM */ template SCM to_scm(const T& val) { using Type = std::remove_const_t; if constexpr (std::is_same_v || std::is_same_v) return scm_from_utf8_stringn(val.data(), val.size()); else if constexpr (is_char_array_v|| std::is_same_v) return scm_from_utf8_string(val); else if constexpr (std::is_same_v) return scm_from_bool(val); else if constexpr (std::is_same_v) return scm_from_size_t(val); else if constexpr (std::is_same_v) return scm_from_int64(val); else if constexpr (std::is_same_v) return scm_from_uint64(val); else if constexpr (std::is_same_v) return val; else { static_assert(always_false::value, "source type not supported"); return SCM_UNSPECIFIED; } } // base case. static inline SCM alist_add(SCM alist) { return alist; } /** * Add key-value pair to an alist * * This assumes that keys are unique ("acons") * * @param alist some alist * @param key key * @param val value * @param keyvals... 0 or more key, value parmeters * * @return the updated alist */ template static inline SCM alist_add(SCM alist, const Key& key, const Value& val, KeyVals... keyvals) { SCM res = scm_acons(to_scm(key), to_scm(val), alist); return alist_add(res, std::forward(keyvals)...); } /** * Make an SCM error * * @param err name of the error * @param subr function name * @param frm... args format string * * @return an error (type) */ template void raise_error(const std::string& err, const std::string& subr, fmt::format_string frm, T&&... args) noexcept { static SCM mu_scm_error = scm_from_utf8_symbol("mu-scm-error"); scm_error(mu_scm_error, subr.c_str(), fmt::format(frm, std::forward(args)...).c_str(), SCM_BOOL_F, SCM_BOOL_F); } /**@}*/ } /** * SCM formatter, for use with fmt * * @param scm some object * * @return string representation of scm */ // static inline std::string format_as(SCM scm) { // return Mu::Scm::from_scm(scm_object_to_string(scm, SCM_UNSPECIFIED)); // } // XXX doesn't work: // "static assertion failed: Formatting of non-void pointers is disallowed" #endif /*MU_SCM_HH*/