1use std::sync::Arc;
14
15use cljrs_eval::GlobalEnv;
16#[cfg(not(target_arch = "wasm32"))]
17use cljrs_gc::GcConfig;
18
19mod core_async;
20#[cfg(not(target_arch = "wasm32"))]
22mod edn;
23#[cfg(not(target_arch = "wasm32"))]
24pub mod io;
25mod set;
26mod string;
27const CLOJURE_TEST_SRC: &str = include_str!("clojure/test.cljrs");
30const CLOJURE_STRING_SRC: &str = include_str!("clojure/string.cljrs");
31const CLOJURE_SET_SRC: &str = include_str!("clojure/set.cljrs");
32const CLOJURE_TEMPLATE_SRC: &str = include_str!("clojure/template.cljrs");
33#[cfg(not(target_arch = "wasm32"))]
34const CLOJURE_RUST_IO_SRC: &str = include_str!("clojure/rust/io.cljrs");
35#[cfg(not(target_arch = "wasm32"))]
36const CLOJURE_EDN_SRC: &str = include_str!("clojure/edn.cljrs");
37const CLOJURE_WALK_SRC: &str = include_str!("clojure/walk.cljrs");
38const CLOJURE_DATA_SRC: &str = include_str!("clojure/data.cljrs");
39const COLJURE_ZIP_SRC: &str = include_str!("clojure/zip.cljrs");
40
41macro_rules! register_fns {
46 ($globals:expr, $ns:expr, [ $( ($name:expr, $arity:expr, $func:expr) ),* $(,)? ]) => {{
47 use cljrs_gc::GcPtr;
48 use cljrs_value::{NativeFn, Value};
49 let ns: &str = $ns;
50 $(
51 {
52 let nf = NativeFn::new($name, $arity, $func);
53 $globals.intern(ns, std::sync::Arc::from($name), Value::NativeFunction(GcPtr::new(nf)));
54 }
55 )*
56 }};
57}
58
59pub(crate) use register_fns;
60
61pub fn register(globals: &Arc<GlobalEnv>) {
70 string::register(globals, "clojure.string");
73 globals.register_builtin_source("clojure.string", CLOJURE_STRING_SRC);
74
75 set::register(globals, "clojure.set");
77 globals.register_builtin_source("clojure.set", CLOJURE_SET_SRC);
78
79 globals.register_builtin_source("clojure.template", CLOJURE_TEMPLATE_SRC);
81
82 globals.register_builtin_source("clojure.test", CLOJURE_TEST_SRC);
84
85 #[cfg(not(target_arch = "wasm32"))]
87 {
88 io::register(globals, "clojure.rust.io");
89 globals.register_builtin_source("clojure.rust.io", CLOJURE_RUST_IO_SRC);
90
91 edn::register(globals, "clojure.edn");
92 globals.register_builtin_source("clojure.edn", CLOJURE_EDN_SRC);
93 }
94
95 globals.register_builtin_source("clojure.walk", CLOJURE_WALK_SRC);
97
98 globals.register_builtin_source("clojure.data", CLOJURE_DATA_SRC);
100
101 globals.register_builtin_source("clojure.zip", COLJURE_ZIP_SRC);
103}
104
105#[cfg(not(target_arch = "wasm32"))]
115pub fn standard_env_no_ir() -> Arc<GlobalEnv> {
116 let globals = cljrs_eval::standard_env_minimal_no_ir();
117 register(&globals);
118
119 cljrs_gc::HEAP.set_config_from_env();
120 let roots_gc = globals.clone();
121 cljrs_gc::HEAP.register_root_tracer(move |visitor| {
122 use cljrs_gc::GcVisitor as _;
123 let namespaces = roots_gc.namespaces.read().unwrap();
124 for (_name, ns_ptr) in namespaces.iter() {
125 visitor.visit(ns_ptr);
126 }
127 });
128
129 globals
130}
131
132#[cfg(not(target_arch = "wasm32"))]
138pub fn standard_env() -> Arc<GlobalEnv> {
139 let globals = cljrs_eval::standard_env_minimal();
140 register(&globals);
141
142 cljrs_gc::HEAP.set_config_from_env();
146 let roots_gc = globals.clone();
147 cljrs_gc::HEAP.register_root_tracer(move |visitor| {
148 use cljrs_gc::GcVisitor as _;
149 let namespaces = roots_gc.namespaces.read().unwrap();
150 for (_name, ns_ptr) in namespaces.iter() {
151 visitor.visit(ns_ptr);
152 }
153 });
154
155 cljrs_eval::mark_compiler_ready(&globals);
157
158 globals
159}
160
161#[cfg(not(target_arch = "wasm32"))]
163pub fn standard_env_with_paths(source_paths: Vec<std::path::PathBuf>) -> Arc<GlobalEnv> {
164 let globals = standard_env();
165 globals.set_source_paths(source_paths);
166 globals
167}
168
169#[cfg(not(target_arch = "wasm32"))]
171pub fn standard_env_with_paths_and_config(
172 source_paths: Vec<std::path::PathBuf>,
173 gc_config: Arc<GcConfig>,
174) -> Arc<GlobalEnv> {
175 let globals = standard_env();
176 globals.set_source_paths(source_paths);
177 globals.set_gc_config(gc_config.clone());
178 cljrs_gc::HEAP.set_config(gc_config);
181 globals
182}
183
184#[cfg(test)]
187mod tests {
188 use super::*;
189 use cljrs_eval::{Env, EvalResult, eval};
190 use cljrs_reader::Parser;
191 use cljrs_value::Value;
192
193 fn make_env() -> (Arc<GlobalEnv>, Env) {
194 let globals = standard_env();
195 let env = Env::new(globals.clone(), "user");
196 (globals, env)
197 }
198
199 #[allow(clippy::result_large_err)]
200 fn run(src: &str, env: &mut Env) -> EvalResult {
201 let mut parser = Parser::new(src.to_string(), "<test>".to_string());
202 let forms = parser.parse_all().expect("parse error");
203 let mut result = Value::Nil;
204 for form in forms {
205 result = eval(&form, env)?;
206 }
207 Ok(result)
208 }
209
210 #[test]
213 fn test_string_upper_lower() {
214 let (_, mut env) = make_env();
215 run("(require '[clojure.string :as str])", &mut env).unwrap();
216 assert_eq!(
217 run("(str/upper-case \"hello\")", &mut env).unwrap(),
218 Value::string("HELLO")
219 );
220 assert_eq!(
221 run("(str/lower-case \"WORLD\")", &mut env).unwrap(),
222 Value::string("world")
223 );
224 }
225
226 #[test]
227 fn test_string_trim() {
228 let (_, mut env) = make_env();
229 run("(require '[clojure.string :as str])", &mut env).unwrap();
230 assert_eq!(
231 run("(str/trim \" hello \")", &mut env).unwrap(),
232 Value::string("hello")
233 );
234 assert_eq!(
235 run("(str/triml \" hi\")", &mut env).unwrap(),
236 Value::string("hi")
237 );
238 assert_eq!(
239 run("(str/trimr \"hi \")", &mut env).unwrap(),
240 Value::string("hi")
241 );
242 }
243
244 #[test]
245 fn test_string_predicates() {
246 let (_, mut env) = make_env();
247 run("(require '[clojure.string :as str])", &mut env).unwrap();
248 assert_eq!(
249 run("(str/blank? \" \")", &mut env).unwrap(),
250 Value::Bool(true)
251 );
252 assert_eq!(
253 run("(str/blank? \"x\")", &mut env).unwrap(),
254 Value::Bool(false)
255 );
256 assert_eq!(
257 run("(str/starts-with? \"hello\" \"hel\")", &mut env).unwrap(),
258 Value::Bool(true)
259 );
260 assert_eq!(
261 run("(str/ends-with? \"hello\" \"llo\")", &mut env).unwrap(),
262 Value::Bool(true)
263 );
264 assert_eq!(
265 run("(str/includes? \"hello\" \"ell\")", &mut env).unwrap(),
266 Value::Bool(true)
267 );
268 }
269
270 #[test]
271 fn test_string_replace() {
272 let (_, mut env) = make_env();
273 run("(require '[clojure.string :as str])", &mut env).unwrap();
274 assert_eq!(
275 run("(str/replace \"aabbcc\" \"bb\" \"XX\")", &mut env).unwrap(),
276 Value::string("aaXXcc")
277 );
278 assert_eq!(
279 run("(str/replace-first \"aabbcc\" \"a\" \"X\")", &mut env).unwrap(),
280 Value::string("Xabbcc")
281 );
282 }
283
284 #[test]
285 fn test_string_split_join() {
286 let (_, mut env) = make_env();
287 run("(require '[clojure.string :as str])", &mut env).unwrap();
288 let v = run("(str/split \"a,b,c\" \",\")", &mut env).unwrap();
289 assert!(matches!(v, Value::Vector(_)));
290 assert_eq!(
291 run("(str/join \"-\" [\"a\" \"b\" \"c\"])", &mut env).unwrap(),
292 Value::string("a-b-c")
293 );
294 }
295
296 #[test]
297 fn test_string_capitalize() {
298 let (_, mut env) = make_env();
299 run("(require '[clojure.string :as str])", &mut env).unwrap();
300 assert_eq!(
301 run("(str/capitalize \"hello world\")", &mut env).unwrap(),
302 Value::string("Hello world")
303 );
304 }
305
306 #[test]
307 fn test_string_split_lines() {
308 let (_, mut env) = make_env();
309 run("(require '[clojure.string :as str])", &mut env).unwrap();
310 let v = run("(str/split-lines \"a\\nb\\nc\")", &mut env).unwrap();
311 assert!(matches!(v, Value::Vector(_)));
312 }
313
314 #[test]
317 fn test_set_union() {
318 let (_, mut env) = make_env();
319 run("(require '[clojure.set :as s])", &mut env).unwrap();
320 let v = run("(s/union #{1 2} #{2 3})", &mut env).unwrap();
321 match v {
322 Value::Set(s) => assert_eq!(s.count(), 3),
323 other => panic!("expected set, got {other:?}"),
324 }
325 }
326
327 #[test]
328 fn test_set_intersection() {
329 let (_, mut env) = make_env();
330 run("(require '[clojure.set :as s])", &mut env).unwrap();
331 let v = run("(s/intersection #{1 2 3} #{2 3 4})", &mut env).unwrap();
332 match v {
333 Value::Set(s) => assert_eq!(s.count(), 2),
334 other => panic!("expected set, got {other:?}"),
335 }
336 }
337
338 #[test]
339 fn test_set_difference() {
340 let (_, mut env) = make_env();
341 run("(require '[clojure.set :as s])", &mut env).unwrap();
342 let v = run("(s/difference #{1 2 3} #{2 3})", &mut env).unwrap();
343 match v {
344 Value::Set(s) => assert_eq!(s.count(), 1),
345 other => panic!("expected set, got {other:?}"),
346 }
347 }
348
349 #[test]
350 fn test_set_subset_superset() {
351 let (_, mut env) = make_env();
352 run("(require '[clojure.set :as s])", &mut env).unwrap();
353 assert_eq!(
354 run("(s/subset? #{1 2} #{1 2 3})", &mut env).unwrap(),
355 Value::Bool(true)
356 );
357 assert_eq!(
358 run("(s/superset? #{1 2 3} #{1 2})", &mut env).unwrap(),
359 Value::Bool(true)
360 );
361 }
362
363 #[test]
364 fn test_set_map_invert() {
365 let (_, mut env) = make_env();
366 run("(require '[clojure.set :as s])", &mut env).unwrap();
367 let v = run("(s/map-invert {:a 1 :b 2})", &mut env).unwrap();
368 assert!(matches!(v, Value::Map(_)));
369 }
370
371 #[test]
374 fn test_clojure_test_lazy_load() {
375 std::thread::Builder::new()
379 .stack_size(16 * 1024 * 1024)
380 .spawn(|| {
381 let (_, mut env) = make_env();
382 run(
385 "(require '[clojure.test :refer [is deftest run-tests]])",
386 &mut env,
387 )
388 .unwrap();
389 let v = run("(is (= 1 1))", &mut env).unwrap();
390 assert_eq!(v, Value::Bool(true));
391 })
392 .unwrap()
393 .join()
394 .unwrap();
395 }
396}