1use std::sync::Arc;
14
15use cljrs_eval::GlobalEnv;
16use cljrs_gc::GcConfig;
17
18mod core_async;
19mod edn;
20pub mod io;
21mod set;
22mod string;
23const CLOJURE_TEST_SRC: &str = include_str!("clojure/test.cljrs");
26const CLOJURE_STRING_SRC: &str = include_str!("clojure/string.cljrs");
27const CLOJURE_SET_SRC: &str = include_str!("clojure/set.cljrs");
28const CLOJURE_TEMPLATE_SRC: &str = include_str!("clojure/template.cljrs");
29const CLOJURE_RUST_IO_SRC: &str = include_str!("clojure/rust/io.cljrs");
30const CLOJURE_EDN_SRC: &str = include_str!("clojure/edn.cljrs");
31const CLOJURE_WALK_SRC: &str = include_str!("clojure/walk.cljrs");
32const CLOJURE_DATA_SRC: &str = include_str!("clojure/data.cljrs");
33const COLJURE_ZIP_SRC: &str = include_str!("clojure/zip.cljrs");
34
35macro_rules! register_fns {
40 ($globals:expr, $ns:expr, [ $( ($name:expr, $arity:expr, $func:expr) ),* $(,)? ]) => {{
41 use cljrs_gc::GcPtr;
42 use cljrs_value::{NativeFn, Value};
43 let ns: &str = $ns;
44 $(
45 {
46 let nf = NativeFn::new($name, $arity, $func);
47 $globals.intern(ns, std::sync::Arc::from($name), Value::NativeFunction(GcPtr::new(nf)));
48 }
49 )*
50 }};
51}
52
53pub(crate) use register_fns;
54
55pub fn register(globals: &Arc<GlobalEnv>) {
64 string::register(globals, "clojure.string");
67 globals.register_builtin_source("clojure.string", CLOJURE_STRING_SRC);
68
69 set::register(globals, "clojure.set");
71 globals.register_builtin_source("clojure.set", CLOJURE_SET_SRC);
72
73 globals.register_builtin_source("clojure.template", CLOJURE_TEMPLATE_SRC);
75
76 globals.register_builtin_source("clojure.test", CLOJURE_TEST_SRC);
78
79 io::register(globals, "clojure.rust.io");
81 globals.register_builtin_source("clojure.rust.io", CLOJURE_RUST_IO_SRC);
82
83 edn::register(globals, "clojure.edn");
85 globals.register_builtin_source("clojure.edn", CLOJURE_EDN_SRC);
86
87 globals.register_builtin_source("clojure.walk", CLOJURE_WALK_SRC);
89
90 globals.register_builtin_source("clojure.data", CLOJURE_DATA_SRC);
92
93 globals.register_builtin_source("clojure.zip", COLJURE_ZIP_SRC);
95}
96
97#[cfg(feature = "prebuild-ir")]
101static PREBUILT_IR: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/core_ir.bin"));
102
103pub fn standard_env() -> Arc<GlobalEnv> {
109 let globals = cljrs_eval::standard_env_minimal();
110 register(&globals);
111
112 #[cfg(feature = "prebuild-ir")]
114 {
115 match cljrs_ir::deserialize_bundle(PREBUILT_IR) {
116 Ok(bundle) if !bundle.is_empty() => {
117 cljrs_eval::register_compiler_sources(&globals);
120
121 let loaded = cljrs_eval::load_prebuilt_ir(&globals, &bundle);
122 cljrs_logging::feat_debug!(
123 "ir",
124 "loaded {loaded} prebuilt IR arities from bundle ({} entries)",
125 bundle.len()
126 );
127
128 let g = globals.clone();
131 let _ = std::thread::Builder::new()
132 .stack_size(16 * 1024 * 1024)
133 .spawn(move || {
134 let mut env = cljrs_eval::Env::new(g.clone(), "user");
135 cljrs_eval::ensure_compiler_loaded(&g, &mut env);
136 });
137
138 return globals;
139 }
140 _ => {
141 }
143 }
144 }
145
146 cljrs_eval::register_compiler_sources(&globals);
150 {
151 let g = globals.clone();
152 let _ = std::thread::Builder::new()
153 .stack_size(16 * 1024 * 1024)
154 .spawn(move || {
155 let mut env = cljrs_eval::Env::new(g.clone(), "user");
156 cljrs_eval::ensure_compiler_loaded(&g, &mut env);
157 })
158 .and_then(|h| h.join().map_err(|_| std::io::Error::other("join failed")));
159 }
160
161 globals
162}
163
164pub fn standard_env_with_paths(source_paths: Vec<std::path::PathBuf>) -> Arc<GlobalEnv> {
166 let globals = standard_env();
167 globals.set_source_paths(source_paths);
168 globals
169}
170
171pub fn standard_env_with_paths_and_config(
173 source_paths: Vec<std::path::PathBuf>,
174 gc_config: Arc<GcConfig>,
175) -> Arc<GlobalEnv> {
176 let globals = standard_env();
177 globals.set_source_paths(source_paths);
178 globals.set_gc_config(gc_config.clone());
179 cljrs_gc::HEAP.set_config(gc_config);
181 let roots_globals = globals.clone();
184 cljrs_gc::HEAP.register_root_tracer(move |visitor| {
185 use cljrs_gc::GcVisitor as _;
186 let namespaces = roots_globals.namespaces.read().unwrap();
187 for (_name, ns_ptr) in namespaces.iter() {
188 visitor.visit(ns_ptr);
189 }
190 });
191 globals
192}
193
194#[cfg(test)]
197mod tests {
198 use super::*;
199 use cljrs_eval::{Env, EvalResult, eval};
200 use cljrs_reader::Parser;
201 use cljrs_value::Value;
202
203 fn make_env() -> (Arc<GlobalEnv>, Env) {
204 let globals = standard_env();
205 let env = Env::new(globals.clone(), "user");
206 (globals, env)
207 }
208
209 fn run(src: &str, env: &mut Env) -> EvalResult {
210 let mut parser = Parser::new(src.to_string(), "<test>".to_string());
211 let forms = parser.parse_all().expect("parse error");
212 let mut result = Value::Nil;
213 for form in forms {
214 result = eval(&form, env)?;
215 }
216 Ok(result)
217 }
218
219 #[test]
222 fn test_string_upper_lower() {
223 let (_, mut env) = make_env();
224 run("(require '[clojure.string :as str])", &mut env).unwrap();
225 assert_eq!(
226 run("(str/upper-case \"hello\")", &mut env).unwrap(),
227 Value::string("HELLO")
228 );
229 assert_eq!(
230 run("(str/lower-case \"WORLD\")", &mut env).unwrap(),
231 Value::string("world")
232 );
233 }
234
235 #[test]
236 fn test_string_trim() {
237 let (_, mut env) = make_env();
238 run("(require '[clojure.string :as str])", &mut env).unwrap();
239 assert_eq!(
240 run("(str/trim \" hello \")", &mut env).unwrap(),
241 Value::string("hello")
242 );
243 assert_eq!(
244 run("(str/triml \" hi\")", &mut env).unwrap(),
245 Value::string("hi")
246 );
247 assert_eq!(
248 run("(str/trimr \"hi \")", &mut env).unwrap(),
249 Value::string("hi")
250 );
251 }
252
253 #[test]
254 fn test_string_predicates() {
255 let (_, mut env) = make_env();
256 run("(require '[clojure.string :as str])", &mut env).unwrap();
257 assert_eq!(
258 run("(str/blank? \" \")", &mut env).unwrap(),
259 Value::Bool(true)
260 );
261 assert_eq!(
262 run("(str/blank? \"x\")", &mut env).unwrap(),
263 Value::Bool(false)
264 );
265 assert_eq!(
266 run("(str/starts-with? \"hello\" \"hel\")", &mut env).unwrap(),
267 Value::Bool(true)
268 );
269 assert_eq!(
270 run("(str/ends-with? \"hello\" \"llo\")", &mut env).unwrap(),
271 Value::Bool(true)
272 );
273 assert_eq!(
274 run("(str/includes? \"hello\" \"ell\")", &mut env).unwrap(),
275 Value::Bool(true)
276 );
277 }
278
279 #[test]
280 fn test_string_replace() {
281 let (_, mut env) = make_env();
282 run("(require '[clojure.string :as str])", &mut env).unwrap();
283 assert_eq!(
284 run("(str/replace \"aabbcc\" \"bb\" \"XX\")", &mut env).unwrap(),
285 Value::string("aaXXcc")
286 );
287 assert_eq!(
288 run("(str/replace-first \"aabbcc\" \"a\" \"X\")", &mut env).unwrap(),
289 Value::string("Xabbcc")
290 );
291 }
292
293 #[test]
294 fn test_string_split_join() {
295 let (_, mut env) = make_env();
296 run("(require '[clojure.string :as str])", &mut env).unwrap();
297 let v = run("(str/split \"a,b,c\" \",\")", &mut env).unwrap();
298 assert!(matches!(v, Value::Vector(_)));
299 assert_eq!(
300 run("(str/join \"-\" [\"a\" \"b\" \"c\"])", &mut env).unwrap(),
301 Value::string("a-b-c")
302 );
303 }
304
305 #[test]
306 fn test_string_capitalize() {
307 let (_, mut env) = make_env();
308 run("(require '[clojure.string :as str])", &mut env).unwrap();
309 assert_eq!(
310 run("(str/capitalize \"hello world\")", &mut env).unwrap(),
311 Value::string("Hello world")
312 );
313 }
314
315 #[test]
316 fn test_string_split_lines() {
317 let (_, mut env) = make_env();
318 run("(require '[clojure.string :as str])", &mut env).unwrap();
319 let v = run("(str/split-lines \"a\\nb\\nc\")", &mut env).unwrap();
320 assert!(matches!(v, Value::Vector(_)));
321 }
322
323 #[test]
326 fn test_set_union() {
327 let (_, mut env) = make_env();
328 run("(require '[clojure.set :as s])", &mut env).unwrap();
329 let v = run("(s/union #{1 2} #{2 3})", &mut env).unwrap();
330 match v {
331 Value::Set(s) => assert_eq!(s.count(), 3),
332 other => panic!("expected set, got {other:?}"),
333 }
334 }
335
336 #[test]
337 fn test_set_intersection() {
338 let (_, mut env) = make_env();
339 run("(require '[clojure.set :as s])", &mut env).unwrap();
340 let v = run("(s/intersection #{1 2 3} #{2 3 4})", &mut env).unwrap();
341 match v {
342 Value::Set(s) => assert_eq!(s.count(), 2),
343 other => panic!("expected set, got {other:?}"),
344 }
345 }
346
347 #[test]
348 fn test_set_difference() {
349 let (_, mut env) = make_env();
350 run("(require '[clojure.set :as s])", &mut env).unwrap();
351 let v = run("(s/difference #{1 2 3} #{2 3})", &mut env).unwrap();
352 match v {
353 Value::Set(s) => assert_eq!(s.count(), 1),
354 other => panic!("expected set, got {other:?}"),
355 }
356 }
357
358 #[test]
359 fn test_set_subset_superset() {
360 let (_, mut env) = make_env();
361 run("(require '[clojure.set :as s])", &mut env).unwrap();
362 assert_eq!(
363 run("(s/subset? #{1 2} #{1 2 3})", &mut env).unwrap(),
364 Value::Bool(true)
365 );
366 assert_eq!(
367 run("(s/superset? #{1 2 3} #{1 2})", &mut env).unwrap(),
368 Value::Bool(true)
369 );
370 }
371
372 #[test]
373 fn test_set_map_invert() {
374 let (_, mut env) = make_env();
375 run("(require '[clojure.set :as s])", &mut env).unwrap();
376 let v = run("(s/map-invert {:a 1 :b 2})", &mut env).unwrap();
377 assert!(matches!(v, Value::Map(_)));
378 }
379
380 #[test]
383 fn test_clojure_test_lazy_load() {
384 std::thread::Builder::new()
388 .stack_size(16 * 1024 * 1024)
389 .spawn(|| {
390 let (_, mut env) = make_env();
391 run(
394 "(require '[clojure.test :refer [is deftest run-tests]])",
395 &mut env,
396 )
397 .unwrap();
398 let v = run("(is (= 1 1))", &mut env).unwrap();
399 assert_eq!(v, Value::Bool(true));
400 })
401 .unwrap()
402 .join()
403 .unwrap();
404 }
405}