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
97pub fn standard_env() -> Arc<GlobalEnv> {
103 let globals = cljrs_eval::standard_env_minimal();
104 register(&globals);
105 globals
106}
107
108pub fn standard_env_with_paths(source_paths: Vec<std::path::PathBuf>) -> Arc<GlobalEnv> {
110 let globals = standard_env();
111 globals.set_source_paths(source_paths);
112 globals
113}
114
115pub fn standard_env_with_paths_and_config(
117 source_paths: Vec<std::path::PathBuf>,
118 gc_config: Arc<GcConfig>,
119) -> Arc<GlobalEnv> {
120 let globals = standard_env();
121 globals.set_source_paths(source_paths);
122 globals.set_gc_config(gc_config.clone());
123 cljrs_gc::HEAP.set_config(gc_config);
125 let roots_globals = globals.clone();
128 cljrs_gc::HEAP.register_root_tracer(move |visitor| {
129 use cljrs_gc::GcVisitor as _;
130 let namespaces = roots_globals.namespaces.read().unwrap();
131 for (_name, ns_ptr) in namespaces.iter() {
132 visitor.visit(ns_ptr);
133 }
134 });
135 globals
136}
137
138#[cfg(test)]
141mod tests {
142 use super::*;
143 use cljrs_eval::{Env, EvalResult, eval};
144 use cljrs_reader::Parser;
145 use cljrs_value::Value;
146
147 fn make_env() -> (Arc<GlobalEnv>, Env) {
148 let globals = standard_env();
149 let env = Env::new(globals.clone(), "user");
150 (globals, env)
151 }
152
153 fn run(src: &str, env: &mut Env) -> EvalResult {
154 let mut parser = Parser::new(src.to_string(), "<test>".to_string());
155 let forms = parser.parse_all().expect("parse error");
156 let mut result = Value::Nil;
157 for form in forms {
158 result = eval(&form, env)?;
159 }
160 Ok(result)
161 }
162
163 #[test]
166 fn test_string_upper_lower() {
167 let (_, mut env) = make_env();
168 run("(require '[clojure.string :as str])", &mut env).unwrap();
169 assert_eq!(
170 run("(str/upper-case \"hello\")", &mut env).unwrap(),
171 Value::string("HELLO")
172 );
173 assert_eq!(
174 run("(str/lower-case \"WORLD\")", &mut env).unwrap(),
175 Value::string("world")
176 );
177 }
178
179 #[test]
180 fn test_string_trim() {
181 let (_, mut env) = make_env();
182 run("(require '[clojure.string :as str])", &mut env).unwrap();
183 assert_eq!(
184 run("(str/trim \" hello \")", &mut env).unwrap(),
185 Value::string("hello")
186 );
187 assert_eq!(
188 run("(str/triml \" hi\")", &mut env).unwrap(),
189 Value::string("hi")
190 );
191 assert_eq!(
192 run("(str/trimr \"hi \")", &mut env).unwrap(),
193 Value::string("hi")
194 );
195 }
196
197 #[test]
198 fn test_string_predicates() {
199 let (_, mut env) = make_env();
200 run("(require '[clojure.string :as str])", &mut env).unwrap();
201 assert_eq!(
202 run("(str/blank? \" \")", &mut env).unwrap(),
203 Value::Bool(true)
204 );
205 assert_eq!(
206 run("(str/blank? \"x\")", &mut env).unwrap(),
207 Value::Bool(false)
208 );
209 assert_eq!(
210 run("(str/starts-with? \"hello\" \"hel\")", &mut env).unwrap(),
211 Value::Bool(true)
212 );
213 assert_eq!(
214 run("(str/ends-with? \"hello\" \"llo\")", &mut env).unwrap(),
215 Value::Bool(true)
216 );
217 assert_eq!(
218 run("(str/includes? \"hello\" \"ell\")", &mut env).unwrap(),
219 Value::Bool(true)
220 );
221 }
222
223 #[test]
224 fn test_string_replace() {
225 let (_, mut env) = make_env();
226 run("(require '[clojure.string :as str])", &mut env).unwrap();
227 assert_eq!(
228 run("(str/replace \"aabbcc\" \"bb\" \"XX\")", &mut env).unwrap(),
229 Value::string("aaXXcc")
230 );
231 assert_eq!(
232 run("(str/replace-first \"aabbcc\" \"a\" \"X\")", &mut env).unwrap(),
233 Value::string("Xabbcc")
234 );
235 }
236
237 #[test]
238 fn test_string_split_join() {
239 let (_, mut env) = make_env();
240 run("(require '[clojure.string :as str])", &mut env).unwrap();
241 let v = run("(str/split \"a,b,c\" \",\")", &mut env).unwrap();
242 assert!(matches!(v, Value::Vector(_)));
243 assert_eq!(
244 run("(str/join \"-\" [\"a\" \"b\" \"c\"])", &mut env).unwrap(),
245 Value::string("a-b-c")
246 );
247 }
248
249 #[test]
250 fn test_string_capitalize() {
251 let (_, mut env) = make_env();
252 run("(require '[clojure.string :as str])", &mut env).unwrap();
253 assert_eq!(
254 run("(str/capitalize \"hello world\")", &mut env).unwrap(),
255 Value::string("Hello world")
256 );
257 }
258
259 #[test]
260 fn test_string_split_lines() {
261 let (_, mut env) = make_env();
262 run("(require '[clojure.string :as str])", &mut env).unwrap();
263 let v = run("(str/split-lines \"a\\nb\\nc\")", &mut env).unwrap();
264 assert!(matches!(v, Value::Vector(_)));
265 }
266
267 #[test]
270 fn test_set_union() {
271 let (_, mut env) = make_env();
272 run("(require '[clojure.set :as s])", &mut env).unwrap();
273 let v = run("(s/union #{1 2} #{2 3})", &mut env).unwrap();
274 match v {
275 Value::Set(s) => assert_eq!(s.count(), 3),
276 other => panic!("expected set, got {other:?}"),
277 }
278 }
279
280 #[test]
281 fn test_set_intersection() {
282 let (_, mut env) = make_env();
283 run("(require '[clojure.set :as s])", &mut env).unwrap();
284 let v = run("(s/intersection #{1 2 3} #{2 3 4})", &mut env).unwrap();
285 match v {
286 Value::Set(s) => assert_eq!(s.count(), 2),
287 other => panic!("expected set, got {other:?}"),
288 }
289 }
290
291 #[test]
292 fn test_set_difference() {
293 let (_, mut env) = make_env();
294 run("(require '[clojure.set :as s])", &mut env).unwrap();
295 let v = run("(s/difference #{1 2 3} #{2 3})", &mut env).unwrap();
296 match v {
297 Value::Set(s) => assert_eq!(s.count(), 1),
298 other => panic!("expected set, got {other:?}"),
299 }
300 }
301
302 #[test]
303 fn test_set_subset_superset() {
304 let (_, mut env) = make_env();
305 run("(require '[clojure.set :as s])", &mut env).unwrap();
306 assert_eq!(
307 run("(s/subset? #{1 2} #{1 2 3})", &mut env).unwrap(),
308 Value::Bool(true)
309 );
310 assert_eq!(
311 run("(s/superset? #{1 2 3} #{1 2})", &mut env).unwrap(),
312 Value::Bool(true)
313 );
314 }
315
316 #[test]
317 fn test_set_map_invert() {
318 let (_, mut env) = make_env();
319 run("(require '[clojure.set :as s])", &mut env).unwrap();
320 let v = run("(s/map-invert {:a 1 :b 2})", &mut env).unwrap();
321 assert!(matches!(v, Value::Map(_)));
322 }
323
324 #[test]
327 fn test_clojure_test_lazy_load() {
328 let (_, mut env) = make_env();
329 run(
332 "(require '[clojure.test :refer [is deftest run-tests]])",
333 &mut env,
334 )
335 .unwrap();
336 let v = run("(is (= 1 1))", &mut env).unwrap();
337 assert_eq!(v, Value::Bool(true));
338 }
339}