1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
|
/*
** Copyright (C) 2025 Dirk-Jan C. Binnema <djcb@djcbsoftware.nl>
**
** 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 <string>
#include <string_view>
#include <type_traits>
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wredundant-decls"
#include <libguile.h>
#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 {
/**
* Start a Guile/SCM REPL
*
* Initialize the Scm sub-system, then start a REPL, based on the
* configuration.
*
* @param store a Store object
* @param opts options; opts.scm.script_path must be Nothing
* @param socket_path if non-empty, run in the background on the
* socket path (Unix domain socket); otherwise, run an
* interactive shell in blocking mode.
*
* @return Ok() or some error
*/
Result<void> run_repl(const Store& store, const Options& opts,
const std::string& socket_path = {});
/**
* Run a Guile/SCM script
*
* Initialize the Scm sub-system, then start run a script,
* based on the configuration.
*
* @param store a Store object
* @param opts options
* @param a path to the script
*
* @return Ok() or some error
*/
Result<void> run_script(const Store& store, const Options& opts,
const std::string& script_path);
/**
* Evaluate a Guile/SCM expression
*
* Initialize the Scm sub-system, then start evaluate some expression
* based on the configuration.
*
* @param store a Store object
* @param opts options
* @param expr some expression to evaluate
*
* @return Ok() or some error
*/
Result<void> run_eval(const Mu::Store& store, const Mu::Options& opts,
const std::string& expr);
/**
* Helpers
*
* @{*/
// https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2022/p2593r0.html
template<typename T> struct always_false : std::false_type {};
template<typename T> constexpr bool is_char_array_v =
std::is_array_v<T> &&
std::is_same_v<std::remove_extent_t<T>, char>;
/**
* C++ Exception to capture an SCM exception
*
* An ScmError is a C++ exception that captures the information for a
* Scm exception to be thrown later at some convenient moment (_after_
* an orderly finishing of the block, so DTORs etc can run.)
*/
struct ScmError {
enum struct Id { WrongType, WrongArg, Error };
ScmError(Id id, const char *subr, int pos, SCM bad_val,
const char *expected):
id{Id::WrongType}, subr{subr}, pos(pos), bad_val{bad_val},
expected{expected} {}
ScmError(const char *subr, const char *msg):
id{Id::Error}, subr{subr}, msg{msg} {}
/**
* This will do a "non-local exit", ie. does not return.
*
*/
[[noreturn]] void throw_scm() const {
/* Enforce exhaustive switch (do _not_ add a default case) */
#pragma GCC diagnostic push
#pragma GCC diagnostic error "-Wswitch"
switch(id) {
case Id::WrongType:
case Id::WrongArg:
scm_wrong_type_arg_msg(subr, pos, bad_val, expected);
break;
case Id::Error:
scm_misc_error(subr, msg, SCM_BOOL_F);
break;
#pragma GCC diagnostic ignored "-Wswitch-default"
#pragma GCC diagnostic pop
}
throw; // never reached, just to appease compiler.
}
private:
const Id id;
const char *subr{};
int pos;
SCM bad_val;
const char *expected;
const char *msg{};
};
/**
* Make SCM symbol from string-like value
*
* @param val some value
*
* @return an SCM symbol
*/
template<typename T>
SCM make_symbol(const T& val){
using Type = std::remove_const_t<T>; // *not* std::remove_const
if constexpr (std::is_same_v<Type, std::string> ||
std::is_same_v<Type, std::string_view>)
return scm_from_utf8_symboln(val.data(), val.size());
else if constexpr (is_char_array_v<Type>|| std::is_same_v<Type, const char*>)
return scm_from_utf8_symbol(val);
else {
static_assert(always_false<Type>::value, "source type not supported");
return SCM_UNSPECIFIED;
}
}
/**
* Get some C++ value from an SCM object, generically.
*
* This throws ScmError in case of any errors.
*
* @param ARG some SCM object
* @param func function where this is used
* @param pos argument number (starting from 1)
* @param expected the expected type
*
* @return C++ value
*/
template<typename T>
T from_scm(SCM ARG, const char *func, int pos) {
const auto ensure=[&](bool pred, SCM ARG, const char *expected) {
if (!pred)
throw ScmError{ScmError::Id::WrongType, func, pos, ARG, expected};
};
using Type = std::remove_const_t<T>; // *not* std::remove_const
if constexpr (std::is_same_v<Type, std::string>) {
ensure(scm_string_p(ARG), ARG, "string");
auto str{scm_to_utf8_string(ARG)};
std::string res{str};
::free(str);
return res;
} else if constexpr (std::is_same_v<Type, char>) {
ensure(scm_char_p(ARG), ARG, "character");
return scm_to_char(ARG);
} else if constexpr (std::is_same_v<Type, bool>) {
ensure(scm_boolean_p(ARG), ARG, "bool");
return scm_to_bool(ARG);
} else if constexpr (std::is_same_v<Type, int>) {
ensure(scm_is_signed_integer(ARG, std::numeric_limits<int>::min(),
std::numeric_limits<int>::max()),
ARG, "integer");
return scm_to_int(ARG);
} else if constexpr (std::is_same_v<Type, uint>) {
ensure(scm_is_unsigned_integer(ARG, std::numeric_limits<uint>::min(),
std::numeric_limits<uint>::max()),
ARG, "unsigned");
return scm_to_uint(ARG);
} else if constexpr (std::is_same_v<Type, SCM>) {
return ARG;
} else {
static_assert(always_false<Type>::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<typename T>
T from_scm_with_default(SCM ARG, const T default_value, const char *func, int pos) {
return (scm_is_bool(ARG) && scm_is_false(ARG)) ? default_value : from_scm<T>(ARG, func, pos);
}
/**
* Get some SCM from a C++ value, generically.
*
* @param val some C++ object
*
* @return an SCM
*/
template<typename T>
SCM to_scm(const T& val) {
using Type = std::remove_const_t<T>;
if constexpr (std::is_same_v<Type, std::string> ||
std::is_same_v<Type, std::string_view>)
return scm_from_utf8_stringn(val.data(), val.size());
else if constexpr (is_char_array_v<Type>|| std::is_same_v<Type, const char*>)
return scm_from_utf8_string(val);
else if constexpr (std::is_same_v<Type, std::vector<std::string>>) {
SCM lst{SCM_EOL};
for (auto it = val.end(); it-- != val.begin();)
lst = scm_cons(to_scm(*it), lst);
return lst;
}
else if constexpr (std::is_same_v<Type, bool>)
return scm_from_bool(val);
else if constexpr (std::is_same_v<Type, size_t>)
return scm_from_size_t(val);
else if constexpr (std::is_same_v<Type, int>)
return scm_from_int(val);
else if constexpr (std::is_same_v<Type, GLogLevelFlags>)
return scm_from_int(static_cast<int>(val));
else if constexpr (std::is_same_v<Type, int64_t>)
return scm_from_int64(val);
else if constexpr (std::is_same_v<Type, uint64_t>)
return scm_from_uint64(val);
else if constexpr (std::is_same_v<Type, SCM>)
return val;
else {
static_assert(always_false<Type>::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<typename Key, typename Value, typename... KeyVals>
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>(keyvals)...);
}
//template<scm_t_catch_body Func, scm_t_catch_handler Handler>
static inline SCM try_scm(scm_t_catch_body func, scm_t_catch_handler handler) {
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wcast-function-type"
return scm_internal_catch(
SCM_BOOL_F,
func, SCM_BOOL_F,
handler, SCM_BOOL_F);
#pragma GCC diagnostic pop
}
/**@}*/
}
#endif /*MU_SCM_HH*/
|