1fn previously_failed_error(registry: &NamespaceRegistry<Value>, namespace: &str) -> String {
2 let mut message =
3 format!("Namespace load previously failed; use explicit reload to retry: {namespace}");
4 if let Some(detail) = registry.load_failure(namespace) {
5 message.push_str(&format!(" (initial failure: {detail})"));
6 }
7 message
8}
9
10fn eval_source_form(
11 namespace: &str,
12 form: &crate::kernel::SpannedForm,
13 env: &mut HashMap<String, Value>,
14) -> Result<Value, String> {
15 let site = ExceptionSite {
16 namespace: Some(namespace.to_owned()),
17 resource: None,
18 line: form.span.start.line,
19 column: form.span.start.column,
20 };
21 let form = attach_exception_sites(form);
22 with_exception_site(site, || eval(&form, env))
23}
24
25fn load_source_namespace(
26 name: &str,
27 source: &str,
28 registry: &NamespaceRegistry<Value>,
29 env: &mut HashMap<String, Value>,
30) -> Result<(), String> {
31 let forms = crate::kernel::read_forms(source).map_err(|error| error.to_string())?;
32 let mut start = 0;
33 if forms
34 .first()
35 .is_some_and(|form| top_level_namespace_form(&form.form))
36 {
37 eval_source_form(name, &forms[0], env)
38 .map_err(|error| format!("{name}: top-level form 1: {error}"))?;
39 start = 1;
40 }
41 let declarations = forms[start..]
42 .iter()
43 .filter_map(|form| top_level_definition_name(&form.form))
44 .map(|name| Form::Symbol(name.to_owned()))
45 .collect::<Vec<_>>();
46 if !declarations.is_empty() {
47 let declaration = Form::List(
48 std::iter::once(Form::Symbol("declare".into()))
49 .chain(declarations)
50 .collect(),
51 );
52 eval(&declaration, env)
53 .map_err(|error| format!("{name}: top-level predeclaration: {error}"))?;
54 }
55 for (index, form) in forms.into_iter().enumerate().skip(start) {
56 eval_source_form(name, &form, env)
57 .map_err(|error| format!("{name}: top-level form {}: {error}", index + 1))?;
58 }
59 if registry.find(name).is_none() {
60 return Err(format!(
61 "Namespace source did not define expected namespace: {name}"
62 ));
63 }
64 Ok(())
65}
66
67fn ensure_namespace(
68 registry: &NamespaceRegistry<Value>,
69 env: &mut HashMap<String, Value>,
70 name: &str,
71 reload: bool,
72) -> Result<(), String> {
73 let load_state = match registry.load_state(name) {
77 Some(NamespaceLoadState::Unloaded) if registry.load_failure(name).is_some() => {
78 Some(NamespaceLoadState::Failed)
79 }
80 state => state,
81 };
82 match load_state {
83 Some(NamespaceLoadState::Loaded) if !reload => return Ok(()),
84 Some(NamespaceLoadState::Loading) => {
85 return Err(format!("Cyclic namespace require: {name}"));
86 }
87 Some(NamespaceLoadState::Failed) if !reload => {
88 return Err(previously_failed_error(registry, name));
89 }
90 _ => {}
91 }
92
93 let catalog = package_catalog();
94 if let Some(coordinate) = catalog.coordinate_for_namespace(name) {
95 if catalog.state(&coordinate).as_deref() != Some("ready") {
96 return Err(format!(
97 "package/not-installed: namespace is locked but unavailable: {name}; call Package/ensure first"
98 ));
99 }
100 }
101
102 let requiring = registry.current().name().as_str().to_owned();
103 let previous_state = match load_state {
109 Some(NamespaceLoadState::Unloaded) if registry.find(name).is_some() => {
110 Some(NamespaceLoadState::Loaded)
111 }
112 Some(state) => Some(state),
113 None => registry.find(name).map(|_| NamespaceLoadState::Loaded),
114 };
115 let registry_before = registry.transaction_snapshot([requiring.as_str(), name]);
116 let environment_before = env
121 .iter()
122 .filter(|(binding, _)| !binding.contains('/'))
123 .map(|(binding, value)| (binding.clone(), value.clone()))
124 .collect::<HashMap<_, _>>();
125 let macros_before = ACTIVE_MACROS.with(|active| {
126 active
127 .borrow()
128 .as_ref()
129 .map(|macros| macros.borrow().clone())
130 });
131 registry.clear_module_dependencies(name);
132 registry.set_load_state(name, NamespaceLoadState::Loading);
133
134 let loaded = (|| {
135 let resource = NAMESPACE_SOURCE_PROVIDER
136 .with(|active| active.borrow().as_ref().and_then(|provider| provider(name)))
137 .ok_or_else(|| format!("Cannot require missing namespace: {name}"))?;
138 #[cfg(all(feature = "direct-native", not(target_arch = "wasm32")))]
139 let loaded_directly = if let Some(loader) = direct_native_namespace_loader() {
140 loader(name, resource.clone(), env)?;
141 true
142 } else {
143 false
144 };
145 #[cfg(not(all(feature = "direct-native", not(target_arch = "wasm32"))))]
146 let loaded_directly = false;
147 if !loaded_directly {
148 let source = match &resource {
149 NamespaceResource::Source(source) => Some(source.clone()),
150 #[cfg(not(target_arch = "wasm32"))]
151 NamespaceResource::SourcePath(_) => {
152 Some(crate::core::read_source_resource(&resource, name)?)
153 }
154 #[cfg(feature = "bytecode-vm")]
155 NamespaceResource::Bytecode { .. } => None,
156 };
157 if let Some(source) = source {
158 load_source_namespace(name, &source, registry, env)?;
159 } else {
160 #[cfg(feature = "bytecode-vm")]
161 if let NamespaceResource::Bytecode {
162 namespace_form,
163 artifact,
164 } = resource
165 {
166 let forms = crate::kernel::read_forms(&namespace_form)
167 .map_err(|error| error.to_string())?;
168 for (index, form) in forms.into_iter().enumerate() {
169 eval_source_form(name, &form, env).map_err(|error| {
170 format!("{name}: namespace form {}: {error}", index + 1)
171 })?;
172 }
173 let program = Rc::new(crate::vm::decode_program(&artifact)?);
174 registry.set_current(name);
175 crate::vm::execute_program_with_globals(program, registry)
176 .map_err(|error| error.to_string())?;
177 }
178 }
179 }
180 if registry.find(name).is_none() {
181 return Err(format!(
182 "Namespace source did not define expected namespace: {name}"
183 ));
184 }
185 Ok(())
186 })();
187
188 select_namespace_environment(registry, env, &requiring);
189 if let Err(error) = loaded {
190 *env = environment_before;
191 registry.restore_transaction(registry_before);
192 refresh_namespace_environment(registry, env);
193 if previous_state == Some(NamespaceLoadState::Loaded) {
194 registry.set_load_state(name, NamespaceLoadState::Loaded);
199 registry.clear_load_failure(name);
200 } else {
201 registry.set_load_state(name, NamespaceLoadState::Failed);
202 registry.set_load_failure(name, error.clone());
203 }
204 if let Some(saved) = macros_before {
205 ACTIVE_MACROS.with(|active| {
206 if let Some(macros) = active.borrow().as_ref() {
207 *macros.borrow_mut() = saved;
208 }
209 });
210 }
211 return Err(error);
212 }
213
214 registry.set_load_state(name, NamespaceLoadState::Loaded);
215 registry.clear_load_failure(name);
216 registry.commit_module_revision(name);
217 Ok(())
218}
219
220fn ensure_foundation_namespace_for_symbol(
221 registry: &NamespaceRegistry<Value>,
222 env: &mut HashMap<String, Value>,
223 symbol: &str,
224) -> Result<(), String> {
225 let Some((namespace, _)) = symbol.split_once('/') else {
226 return Ok(());
227 };
228 if namespace.starts_with("std.foundation.")
229 && registry.load_state(namespace) == Some(NamespaceLoadState::Unloaded)
230 {
231 ensure_namespace(registry, env, namespace, false)?;
232 }
233 Ok(())
234}
235
236fn top_level_namespace_form(form: &Form) -> bool {
237 matches!(form_without_metadata(form), Form::List(values)
238 if matches!(values.first(), Some(Form::Symbol(head)) if head == "ns" || head == "ns+"))
239}
240
241fn top_level_definition_name(form: &Form) -> Option<&str> {
242 let form = match form {
243 Form::Metadata(_, value) => value.as_ref(),
244 value => value,
245 };
246 let Form::List(values) = form else {
247 return None;
248 };
249 let head = match values.first()? {
250 Form::Symbol(head) => head.as_str(),
251 _ => return None,
252 };
253 if !matches!(head, "def" | "defonce" | "defn" | "defmacro") {
254 return None;
255 }
256 match values.get(1)? {
257 Form::Symbol(name) => Some(name),
258 Form::Metadata(_, value) => match value.as_ref() {
259 Form::Symbol(name) => Some(name),
260 _ => None,
261 },
262 _ => None,
263 }
264}
265
266pub fn require_namespace(
267 registry: &NamespaceRegistry<Value>,
268 env: &mut HashMap<String, Value>,
269 name: &str,
270) -> Result<(), String> {
271 ensure_namespace(registry, env, name, false)
272}
273
274fn eval_require_spec(
275 registry: &NamespaceRegistry<Value>,
276 env: &mut HashMap<String, Value>,
277 form: &Form,
278) -> Result<(), String> {
279 let (target, options) = match form {
280 Form::Vector(items) => {
281 let target = match items.first() {
282 Some(Form::Symbol(target)) => target.clone(),
283 _ => return Err("require namespace must be a symbol".into()),
284 };
285 (
286 crate::kernel::generated::normalize_namespace(&target).to_owned(),
287 &items[1..],
288 )
289 }
290 Form::List(items)
291 if items.len() == 2
292 && matches!(&items[0], Form::Symbol(q) if q == "quote")
293 && matches!(&items[1], Form::Symbol(_)) =>
294 {
295 let target = match &items[1] {
296 Form::Symbol(target) => target.clone(),
297 _ => unreachable!(),
298 };
299 (
300 crate::kernel::generated::normalize_namespace(&target).to_owned(),
301 &[][..],
302 )
303 }
304 _ => return Err("require expects vectors such as [chrome.api :as api]".into()),
305 };
306 if options.len() % 2 != 0 {
307 return Err(format!("Malformed require options for {target}"));
308 }
309 let lazy = options.chunks(2).any(|option| {
310 matches!(&option[0], Form::Keyword(keyword) if keyword.as_str() == "lazy")
311 && matches!(&option[1], Form::Bool(true))
312 });
313 let reload = options.chunks(2).any(|option| {
314 matches!(&option[0], Form::Keyword(keyword) if keyword.as_str() == "reload")
315 && matches!(&option[1], Form::Bool(true))
316 });
317 let excluded = options
318 .chunks(2)
319 .find_map(|option| {
320 matches!(&option[0], Form::Keyword(keyword) if keyword.as_str() == "exclude")
321 .then_some(&option[1])
322 })
323 .map(|value| match value {
324 Form::Vector(names) => names
325 .iter()
326 .map(|name| match name {
327 Form::Symbol(name) if name == "/" || !name.contains('/') => Ok(name.clone()),
328 _ => Err("require :exclude expects unqualified symbols".to_string()),
329 })
330 .collect::<Result<HashSet<_>, _>>(),
331 _ => Err("require :exclude expects a vector of symbols".into()),
332 })
333 .transpose()?
334 .unwrap_or_default();
335 if lazy {
336 let has_alias = options
337 .chunks(2)
338 .any(|option| matches!(&option[0], Form::Keyword(keyword) if keyword.as_str() == "as"));
339 if !has_alias {
340 return Err("require :lazy requires :as".into());
341 }
342 for option in options.chunks(2) {
343 match &option[0] {
344 Form::Keyword(keyword)
345 if keyword.as_str() == "refer" || keyword.as_str() == "refer-macros" =>
346 {
347 return Err(format!(
348 "require :lazy cannot be combined with :{}",
349 keyword
350 ));
351 }
352 Form::Keyword(keyword)
353 if keyword.as_str() == "lazy" && !matches!(&option[1], Form::Bool(true)) =>
354 {
355 return Err("require :lazy expects true".into());
356 }
357 _ => {}
358 }
359 }
360 }
361 let deferred = lazy && !reload;
362 #[cfg(all(feature = "direct-native", not(target_arch = "wasm32")))]
363 let direct_reload = !deferred
364 && direct_native_namespace_loader().is_some()
365 && namespace_has_interpreted_functions(registry, &target);
366 #[cfg(not(all(feature = "direct-native", not(target_arch = "wasm32"))))]
367 let direct_reload = false;
368 if deferred {
369 if registry.load_state(&target).is_none() {
370 registry.set_load_state(&target, NamespaceLoadState::Unloaded);
371 }
372 } else if !crate::kernel::generated::known_namespace(&target) || direct_reload {
373 ensure_namespace(registry, env, &target, reload || direct_reload)?;
374 }
375 let requiring = registry.current().name().as_str().to_owned();
376 if requiring != target && registry.load_state(&requiring) == Some(NamespaceLoadState::Loading) {
377 registry.record_module_dependency(&requiring, &target);
378 }
379 if !deferred {
380 let destination = registry.current();
381 for name in &excluded {
382 let local = crate::lang::data::Symbol::parse(name);
383 if destination
384 .resolve(&local)
385 .is_some_and(|var| var.symbol().get_namespace() == Some(target.as_str()))
386 {
387 destination.unmap(&local);
388 env.remove(name);
389 }
390 }
391 }
392 for option in options.chunks(2) {
393 let name = match &option[0] {
394 Form::Keyword(keyword) => keyword.as_str(),
395 _ => return Err("Malformed require options".into()),
396 };
397 match name {
398 "as" => {
399 let alias = match &option[1] {
400 Form::Symbol(alias) if !alias.contains('/') => alias.clone(),
401 _ => return Err("require :as expects an unqualified symbol".into()),
402 };
403 if alias == "-" {
404 return Err("Namespace alias is reserved: -".into());
405 }
406 let local = crate::lang::data::Symbol::parse(&alias);
409 if registry
410 .current()
411 .resolve(&local)
412 .is_some_and(|var| var.symbol().get_namespace() == Some("std.foundation"))
413 {
414 registry.current().unmap(&local);
415 env.remove(&alias);
416 }
417 if deferred {
418 registry.current().lazy_alias(alias, &target);
419 } else {
420 let namespace = registry
421 .find(&target)
422 .ok_or_else(|| format!("Cannot require missing namespace: {target}"))?;
423 registry.current().alias(alias, namespace);
424 }
425 }
426 "refer" => {
427 let source = registry
428 .find(&target)
429 .ok_or_else(|| format!("Cannot require missing namespace: {target}"))?;
430 let destination = registry.current();
431 let destination_name = destination.name().as_str().to_owned();
432 let names = match &option[1] {
433 Form::Keyword(name) if name.as_str() == "all" => source
434 .mappings()
435 .into_iter()
436 .map(|(name, _)| name.as_str().to_owned())
437 .collect::<Vec<_>>(),
438 Form::Vector(names) => names
439 .iter()
440 .map(|name| match name {
441 Form::Symbol(name) if !name.contains('/') => Ok(name.clone()),
442 _ => Err("require :refer expects unqualified symbols".to_string()),
443 })
444 .collect::<Result<Vec<_>, _>>()?,
445 _ => return Err("require :refer expects a vector of symbols or :all".into()),
446 };
447 for name in names {
448 if excluded.contains(&name) {
449 continue;
450 }
451 let var = source
452 .resolve(&crate::lang::data::Symbol::parse(&name))
453 .ok_or_else(|| format!("Cannot refer missing Var: {target}/{name}"))?;
454 destination.map_var(crate::lang::data::Symbol::parse(&name), var);
455 ACTIVE_MACROS.with(|active| {
456 if let Some(macros) = active.borrow().as_ref() {
457 let mut macros = macros.borrow_mut();
458 if let Some(function) =
459 macros.get(&(target.clone(), name.clone())).cloned()
460 {
461 macros.insert((destination_name.clone(), name.clone()), function);
462 }
463 }
464 });
465 }
466 }
467 "refer-macros" => {
468 let Form::Vector(names) = &option[1] else {
469 return Err("require :refer-macros expects a vector of symbols".into());
470 };
471 let destination = registry.current().name().as_str().to_owned();
472 ACTIVE_MACROS.with(|active| -> Result<(), String> {
473 let active = active.borrow();
474 let macros = active
475 .as_ref()
476 .ok_or_else(|| "macro runtime is unavailable".to_string())?;
477 let mut macros = macros.borrow_mut();
478 for name in names {
479 let Form::Symbol(name) = name else {
480 return Err("require :refer-macros expects unqualified symbols".into());
481 };
482 if name.contains('/') {
483 return Err("require :refer-macros expects unqualified symbols".into());
484 }
485 let macro_fn = macros
486 .get(&(target.clone(), name.clone()))
487 .cloned()
488 .ok_or_else(|| {
489 format!("Cannot refer missing macro: {target}/{name}")
490 })?;
491 macros.insert((destination.clone(), name.clone()), macro_fn);
492 }
493 Ok(())
494 })?;
495 }
496 "lazy" => {}
497 "reload" => {
498 if !matches!(&option[1], Form::Bool(true)) {
499 return Err("require :reload expects true".into());
500 }
501 }
502 "exclude" => {}
503 other => return Err(format!("Unsupported require option: :{other}")),
504 }
505 }
506 Ok(())
507}
508
509fn eval_require_specs(
510 registry: &NamespaceRegistry<Value>,
511 env: &mut HashMap<String, Value>,
512 specs: &[Form],
513) -> Result<(), String> {
514 for spec in specs {
515 eval_require_spec(registry, env, spec)?;
516 }
517 refresh_namespace_environment(registry, env);
518 Ok(())
519}
520
521#[cfg(all(feature = "direct-native", not(target_arch = "wasm32")))]
522fn namespace_has_interpreted_functions(registry: &NamespaceRegistry<Value>, name: &str) -> bool {
523 registry.find(name).is_some_and(|namespace| {
524 namespace.mappings().into_iter().any(|(_, var)| {
525 matches!(var.deref_value(), Value::Function(function) if !is_direct_native_function(&function))
526 })
527 })
528}
529
530fn force_lazy_alias(
531 registry: &NamespaceRegistry<Value>,
532 env: &mut HashMap<String, Value>,
533 symbol: &str,
534) -> Result<(), String> {
535 let Some((alias, _)) = symbol.split_once('/') else {
536 return Ok(());
537 };
538 if registry.current().name().as_str() == alias {
539 return Ok(());
540 }
541 let target = registry.current().lazy_target(alias);
542 let Some(target) = target else {
543 return Ok(());
544 };
545 ensure_namespace(registry, env, target.as_str(), false)?;
546 let namespace = registry
547 .find(target.as_str())
548 .ok_or_else(|| format!("Cannot require missing namespace: {target}"))?;
549 registry.current().alias(alias, namespace);
550 refresh_namespace_environment(registry, env);
551 Ok(())
552}
553
554#[inline(never)]
560fn eval_namespace_form(fs: &[Form], env: &mut HashMap<String, Value>) -> Result<Value, String> {
561 let head = match &fs[0] {
562 Form::Symbol(head) => head.as_str(),
563 _ => unreachable!("ns/ns+/require dispatch guarantees a symbol head"),
564 };
565 if head == "require" {
566 let registry = namespace_registry()?;
567 eval_require_specs(®istry, env, &fs[1..])?;
568 return Ok(Value::Nil);
569 }
570 let registry = namespace_registry()?;
571 let (name, clauses) = if head == "ns+" {
572 if matches!(fs.get(1), Some(Form::Symbol(_))) {
573 return Err("ns+ does not accept a namespace name".into());
574 }
575 (registry.current().name().as_str().to_owned(), &fs[1..])
576 } else {
577 if fs.len() < 2 {
578 return Err("ns expects a namespace symbol".into());
579 }
580 let name = match &fs[1] {
581 Form::Symbol(name) if !name.contains('/') => name.clone(),
582 _ => return Err("ns expects a namespace symbol".into()),
583 };
584 (name, &fs[2..])
585 };
586 let config = crate::kernel::GeneratedNamespaceConfig::configure_with(clauses, |_| true)?;
592 if let Some(alias) = config.global_alias() {
593 registry.register_global_alias(alias, &name)?;
594 }
595 for alias in config.declared_global_imports() {
596 let canonical =
597 crate::core::canonical_native_symbol(alias).unwrap_or_else(|| alias.clone());
598 registry.register_global_import(alias, canonical)?;
599 }
600 apply_global_aliases(®istry, &name);
601 crate::core::apply_global_imports(®istry, &name);
602 select_namespace_environment(®istry, env, &name);
603 let destination = registry.current();
604 destination.set_role(config.role());
605 destination.set_foundation_visibility(
606 config.exposed_foundation(),
607 config.excluded_foundation(),
608 config.blank(),
609 );
610 destination.set_native_flavor(config.native_flavor().map(str::to_owned));
611 for (local, module) in config.native_imports() {
612 destination.import(local, module.clone());
613 }
614 for (alias, _) in registry.global_aliases() {
615 destination.unalias(alias.as_str());
616 }
617 for (alias, target) in config.aliases() {
618 if !target.starts_with("std.foundation.") {
619 continue;
620 }
621 if let Some(namespace) = registry.find(&target) {
622 destination.alias(alias, namespace);
623 } else {
624 destination.lazy_alias(alias, target);
625 }
626 }
627 let omitted = match config.exposed_foundation() {
628 Some(exposed) => destination
629 .mappings()
630 .into_iter()
631 .filter(|(local, var)| {
632 var.symbol().get_namespace() == Some("std.foundation")
633 && !exposed.contains(local.as_str())
634 })
635 .map(|(local, _)| local.as_str().to_owned())
636 .collect::<Vec<_>>(),
637 None => config.excluded_foundation().iter().cloned().collect(),
638 };
639 for overridden in omitted {
640 let destination = registry.current();
641 let local = crate::lang::data::Symbol::parse(&overridden);
642 if destination
643 .resolve(&local)
644 .is_some_and(|var| var.symbol().get_namespace() == Some("std.foundation"))
645 {
646 destination.unmap(&local);
647 env.remove(&overridden);
648 }
649 let destination_name = destination.name().as_str().to_owned();
650 ACTIVE_MACROS.with(|active| {
651 if let Some(macros) = active.borrow().as_ref() {
652 macros
653 .borrow_mut()
654 .remove(&(destination_name, overridden.clone()));
655 }
656 });
657 }
658 for (alias, target) in destination.aliases() {
659 let excluded = config
660 .excluded_foundation_libraries()
661 .iter()
662 .any(|library| target.name().as_str() == format!("std.foundation.{library}"));
663 if excluded {
664 destination.unalias(alias.as_str());
665 }
666 }
667 for (alias, target) in destination.lazy_aliases() {
668 let excluded = config
669 .excluded_foundation_libraries()
670 .iter()
671 .any(|library| target.as_str() == format!("std.foundation.{library}"));
672 if excluded {
673 destination.unalias(alias.as_str());
674 }
675 }
676 for library in config.excluded_foundation_libraries() {
677 if let Some(alias) = crate::kernel::generated::foundation_library_alias(library) {
678 destination.unalias(alias);
679 }
680 }
681 for clause in clauses {
682 match clause {
683 Form::List(clause_forms) if matches!(clause_forms.first(), Some(Form::Keyword(k)) if k == "require") =>
684 {
685 eval_require_specs(®istry, env, &clause_forms[1..])?;
686 }
687 Form::List(clause_forms) if matches!(clause_forms.first(), Some(Form::Keyword(k)) if k == "use") =>
688 {
689 let specs = clause_forms[1..]
690 .iter()
691 .map(|namespace| match namespace {
692 Form::Symbol(name) if !name.contains('/') => Ok(Form::Vector(vec![
693 Form::Symbol(name.clone()),
694 Form::Keyword("refer".into()),
695 Form::Keyword("all".into()),
696 ])),
697 _ => Err("ns :use expects namespace symbols".to_string()),
698 })
699 .collect::<Result<Vec<_>, _>>()?;
700 eval_require_specs(®istry, env, &specs)?;
701 }
702 Form::List(clause_forms) if matches!(clause_forms.first(), Some(Form::Keyword(k)) if k == "config") =>
703 {
704 }
709 Form::List(clause_forms) if matches!(clause_forms.first(), Some(Form::Keyword(k)) if k == "flavor" || k == "import") =>
710 {}
711 _ => return Err("unsupported ns clause in evaluator".into()),
712 }
713 }
714 refresh_namespace_environment(®istry, env);
715 Ok(Value::Nil)
716}
717
718pub(crate) fn prepare_namespace_form(form: &Form) -> Result<(), String> {
725 let Form::List(forms) = form_without_metadata(form) else {
726 return Err("namespace declaration must be a list".into());
727 };
728 if !matches!(forms.first(), Some(Form::Symbol(head)) if head == "ns" || head == "ns+") {
729 return Err("namespace declaration must start with ns or ns+".into());
730 }
731 eval_namespace_form(forms, &mut HashMap::new()).map(|_| ())
732}
733
734pub(crate) fn eval_bytecode_management(value: &Value) -> Result<Value, String> {
739 let registry = namespace_registry()?;
740 let mut environment = registry
741 .current()
742 .mappings()
743 .into_iter()
744 .map(|(name, var)| (name.as_str().to_owned(), Value::Var(var)))
745 .collect();
746 refresh_namespace_environment(®istry, &mut environment);
747 let result = eval_bytecode_management_in(value, &mut environment)?;
748 save_namespace_environment(®istry, &mut environment);
749 Ok(result)
750}
751
752pub(crate) fn eval_bytecode_management_in(
758 value: &Value,
759 environment: &mut HashMap<String, Value>,
760) -> Result<Value, String> {
761 let form = value_to_form(value)?;
762 let Form::List(forms) = form_without_metadata(&form) else {
763 return Err("namespace-management instruction expects a list".into());
764 };
765 let Some(Form::Symbol(operator)) = forms.first() else {
766 return Err("namespace-management instruction expects a symbol operator".into());
767 };
768 if !matches!(operator.as_str(), "ns" | "ns+" | "require") {
769 return Err(format!(
770 "namespace-management instruction does not support {operator}"
771 ));
772 }
773 eval_namespace_form(forms, environment)
774}