1use crate::core::{IntrinsicOp, Value};
23use crate::kernel::{Form, Position, Span, SpannedForm};
24use crate::lang::data::List as PList;
25use std::collections::{HashMap, HashSet};
26
27use super::error::{CompileError, CompileErrorKind};
28use super::opcode::Instruction;
29use super::program::{
30 FunctionPrototype, Program, TryEntry, MAX_CONSTANTS, MAX_PRIMITIVE_ARGUMENTS,
31};
32use super::source_map::SourceMap;
33use super::validate::{self, stack_heights};
34
35#[path = "compiler/bindings.rs"]
36mod bindings;
37#[path = "compiler/calls.rs"]
38mod calls;
39#[path = "compiler/coroutines.rs"]
40mod coroutines;
41#[path = "compiler/destructure.rs"]
42mod destructure;
43#[path = "compiler/exceptions.rs"]
44mod exceptions;
45#[path = "compiler/functions.rs"]
46mod functions;
47#[path = "compiler/literals.rs"]
48mod literals;
49#[path = "compiler/scope.rs"]
50mod scope;
51use exceptions::TryContext;
52#[path = "compiler/globals.rs"]
53mod globals;
54#[path = "compiler/recur.rs"]
55mod recur;
56use recur::LoopContext;
57use scope::ScopeStack;
58
59const UNSUPPORTED_OPERATORS: &[&str] = &["in-ns", "await"];
64
65pub fn compile_source(source: &str) -> Result<Program, CompileError> {
70 let forms = crate::kernel::read_forms(source)?;
71 compile_spanned_forms(&forms)
72}
73
74fn compile_spanned_forms(forms: &[SpannedForm]) -> Result<Program, CompileError> {
75 prepare_top_level_namespaces(forms)?;
76 compile_spanned_forms_without_namespace_preparation(forms, HashSet::new(), false)
77}
78
79fn compile_spanned_forms_without_namespace_preparation(
80 forms: &[SpannedForm],
81 excluded_foundation_libraries: HashSet<String>,
82 allow_unbound_globals: bool,
83) -> Result<Program, CompileError> {
84 let mut compiler = Compiler::new(excluded_foundation_libraries, allow_unbound_globals);
85 compiler.predeclare_top_level(forms);
86 let children = compiler.children(forms);
87 compiler.compile_sequence(&children, true)?;
88 compiler.finish()
89}
90
91fn prepare_top_level_namespaces(forms: &[SpannedForm]) -> Result<(), CompileError> {
92 fn prepare(form: &Form, position: Position) -> Result<(), CompileError> {
93 let Form::List(items) = crate::core::form_without_metadata(form) else {
94 return Ok(());
95 };
96 match items.first() {
97 Some(Form::Symbol(operator)) if operator == "ns" || operator == "ns+" => {
98 crate::core::prepare_namespace_form(form).map_err(|message| {
99 CompileError::new(CompileErrorKind::UnsupportedForm, message, Some(position))
100 })
101 }
102 Some(Form::Symbol(operator)) if operator == "do" => {
103 for child in items.iter().skip(1) {
104 prepare(child, position)?;
105 }
106 Ok(())
107 }
108 _ => Ok(()),
109 }
110 }
111
112 for form in forms {
113 prepare(&form.form, form.span.start)?;
114 }
115 Ok(())
116}
117
118pub fn compile_source_with(
123 source: &str,
124 registry: &crate::kernel::NamespaceRegistry<crate::core::Value>,
125) -> Result<Program, CompileError> {
126 let forms = crate::kernel::read_forms(source)?;
127 let config = source_namespace_config(&forms)?;
128 compile_spanned_forms_with_config(&forms, registry, config, true)
129}
130
131pub fn compile_source_with_allow_unbound_globals(
135 source: &str,
136 registry: &crate::kernel::NamespaceRegistry<crate::core::Value>,
137) -> Result<Program, CompileError> {
138 let forms = crate::kernel::read_forms(source)?;
139 let config = source_namespace_config(&forms)?;
140 compile_spanned_forms_with_config_options(&forms, registry, config, true, true)
141}
142
143pub fn compile_source_with_config(
149 source: &str,
150 registry: &crate::kernel::NamespaceRegistry<crate::core::Value>,
151 config: crate::kernel::GeneratedNamespaceConfig,
152) -> Result<Program, CompileError> {
153 let forms = crate::kernel::read_forms(source)?;
154 compile_spanned_forms_with_config(&forms, registry, config, true)
155}
156
157pub fn compile_source_with_config_allow_unbound_globals(
163 source: &str,
164 registry: &crate::kernel::NamespaceRegistry<crate::core::Value>,
165 config: crate::kernel::GeneratedNamespaceConfig,
166) -> Result<Program, CompileError> {
167 let forms = crate::kernel::read_forms(source)?;
168 compile_spanned_forms_with_config_options(&forms, registry, config, true, true)
169}
170
171pub fn compile_form_with_config_allow_unbound_globals(
175 form: Form,
176 registry: &crate::kernel::NamespaceRegistry<crate::core::Value>,
177 config: crate::kernel::GeneratedNamespaceConfig,
178) -> Result<Program, CompileError> {
179 let form = synthetic_spanned_form(form);
180 compile_spanned_form_with_config_allow_unbound_globals(form, registry, config)
181}
182
183pub fn compile_spanned_forms_with_config_allow_unbound_globals(
188 forms: &[SpannedForm],
189 registry: &crate::kernel::NamespaceRegistry<crate::core::Value>,
190 config: crate::kernel::GeneratedNamespaceConfig,
191) -> Result<Program, CompileError> {
192 compile_spanned_forms_with_config_options(forms, registry, config, false, true)
193}
194
195pub fn compile_spanned_form_with_config_allow_unbound_globals(
199 form: SpannedForm,
200 registry: &crate::kernel::NamespaceRegistry<crate::core::Value>,
201 config: crate::kernel::GeneratedNamespaceConfig,
202) -> Result<Program, CompileError> {
203 compile_spanned_forms_with_config_allow_unbound_globals(&[form], registry, config)
204}
205
206pub fn source_uses_dynamic_evaluation(source: &str) -> Result<bool, CompileError> {
212 let forms = crate::kernel::read_forms(source)?;
213
214 fn dynamic_symbol(name: &str) -> bool {
215 matches!(
216 name.rsplit_once('/').map_or(name, |(_, local)| local),
217 "defonce" | "eval" | "eval-in" | "eval-in-ns" | "load-string" | "with-ns"
218 )
219 }
220
221 fn contains(form: &Form) -> bool {
222 match crate::core::form_without_metadata(form) {
223 Form::List(values) => {
224 values.first().is_some_and(
225 |value| matches!(value, Form::Symbol(name) if dynamic_symbol(name)),
226 ) || values.iter().any(contains)
227 }
228 Form::Vector(values) | Form::Set(values) => values.iter().any(contains),
229 Form::Map(entries) => entries
230 .iter()
231 .any(|(key, value)| contains(key) || contains(value)),
232 Form::Tagged(_, value) => contains(value),
233 _ => false,
234 }
235 }
236
237 Ok(forms.iter().any(|form| contains(&form.form)))
238}
239
240fn compile_spanned_forms_with_config(
241 forms: &[SpannedForm],
242 registry: &crate::kernel::NamespaceRegistry<crate::core::Value>,
243 config: crate::kernel::GeneratedNamespaceConfig,
244 prepare_namespaces: bool,
245) -> Result<Program, CompileError> {
246 compile_spanned_forms_with_config_options(forms, registry, config, prepare_namespaces, false)
247}
248
249fn compile_spanned_forms_with_config_options(
250 forms: &[SpannedForm],
251 registry: &crate::kernel::NamespaceRegistry<crate::core::Value>,
252 mut config: crate::kernel::GeneratedNamespaceConfig,
253 prepare_namespaces: bool,
254 allow_unbound_globals: bool,
255) -> Result<Program, CompileError> {
256 crate::core::with_namespace_registry(registry, || {
257 if prepare_namespaces {
258 prepare_top_level_namespaces(forms)?;
259 }
260 sync_registry_global_aliases(&mut config, registry);
261 let rewritten = forms
262 .iter()
263 .map(|form| rewrite_spanned_form(form, &config))
264 .collect::<Vec<_>>();
265 compile_spanned_forms_without_namespace_preparation(
266 &rewritten,
267 config.excluded_foundation_libraries().clone(),
268 allow_unbound_globals,
269 )
270 })
271}
272
273pub fn source_namespace_config(
274 forms: &[SpannedForm],
275) -> Result<crate::kernel::GeneratedNamespaceConfig, CompileError> {
276 let mut selected = None;
277 for form in forms {
278 let crate::kernel::Form::List(items) = crate::core::form_without_metadata(&form.form)
279 else {
280 continue;
281 };
282 let Some(crate::kernel::Form::Symbol(operator)) = items.first() else {
283 continue;
284 };
285 let clause_start = match operator.as_str() {
286 "ns" => 2,
287 "ns+" => 1,
288 _ => continue,
289 };
290 if items.len() < clause_start {
291 return Err(CompileError::new(
292 CompileErrorKind::UnsupportedForm,
293 "namespace declaration is missing its clauses",
294 Some(form.span.start),
295 ));
296 }
297 if operator == "ns" || items.len() > clause_start {
298 selected = Some(
299 crate::kernel::GeneratedNamespaceConfig::configure_with(
300 &items[clause_start..],
301 |_| true,
302 )
303 .map_err(|message| {
304 CompileError::new(
305 CompileErrorKind::UnsupportedForm,
306 message,
307 Some(form.span.start),
308 )
309 })?,
310 );
311 }
312 }
313 Ok(selected.unwrap_or_else(crate::kernel::GeneratedNamespaceConfig::defaults))
314}
315
316fn sync_registry_global_aliases(
317 config: &mut crate::kernel::GeneratedNamespaceConfig,
318 registry: &crate::kernel::NamespaceRegistry<crate::core::Value>,
319) {
320 let excluded_foundation_libraries = config.excluded_foundation_libraries().clone();
321 let excluded_foundation = config.excluded_foundation().clone();
322 let current = registry.current();
323 for library in &excluded_foundation_libraries {
324 if let Some(alias) = crate::kernel::generated::foundation_library_alias(library) {
325 current.unalias(alias);
326 }
327 }
328 for (alias, target) in current.aliases() {
329 let library = target.name().as_str().strip_prefix("std.foundation.");
330 if library.is_some_and(|library| excluded_foundation_libraries.contains(library)) {
331 current.unalias(alias.as_str());
332 }
333 }
334 for (alias, target) in current.lazy_aliases() {
335 let library = target.as_str().strip_prefix("std.foundation.");
336 if library.is_some_and(|library| excluded_foundation_libraries.contains(library)) {
337 current.unalias(alias.as_str());
338 }
339 }
340 config.set_global_aliases(
341 registry
342 .global_aliases()
343 .into_iter()
344 .filter(|(_, namespace)| {
345 let library = namespace
346 .as_str()
347 .strip_prefix("std.foundation.")
348 .unwrap_or_default();
349 !excluded_foundation_libraries.contains(library)
350 && !excluded_foundation.contains(library)
351 })
352 .map(|(alias, namespace)| (alias.as_str().to_owned(), namespace.as_str().to_owned())),
353 );
354}
355
356pub(crate) fn rewrite_spanned_form(
357 form: &SpannedForm,
358 config: &crate::kernel::GeneratedNamespaceConfig,
359) -> SpannedForm {
360 SpannedForm {
361 form: config.rewrite(form.form.clone()),
362 span: form.span.clone(),
363 children: form
364 .children
365 .iter()
366 .map(|child| rewrite_spanned_form(child, config))
367 .collect(),
368 }
369}
370
371pub fn compile_halc_module(
375 module: &crate::kernel::halc::HalcModule,
376 registry: &crate::kernel::NamespaceRegistry<crate::core::Value>,
377) -> Result<Program, CompileError> {
378 let previous = registry.current().name().as_str().to_owned();
379 registry.set_current(&module.namespace);
380 let forms = module
381 .forms
382 .iter()
383 .filter(|form| !top_level_operator(form, "ns"))
384 .cloned()
385 .map(synthetic_spanned_form)
386 .collect::<Vec<_>>();
387 let result = compile_spanned_forms_with_config(
388 &forms,
389 registry,
390 crate::kernel::GeneratedNamespaceConfig::defaults(),
391 false,
392 );
393 registry.set_current(previous);
394 let mut program = result?;
395 program.namespace = Some(module.namespace.clone());
396 program.schema_types = module.schemas.definition_types.clone();
397 program.function_types = module.schemas.function_types.clone();
398 program.inferred_function_types = crate::kernel::schema::infer_function_types(
399 &module.namespace,
400 &module.forms,
401 &program.function_types,
402 &program.schema_types,
403 );
404 validate_declared_function_arities(&program)?;
405 Ok(program)
406}
407
408fn validate_declared_function_arities(program: &Program) -> Result<(), CompileError> {
409 for (index, prototype) in program.functions.iter().enumerate() {
410 let Some(crate::kernel::SchemaType::Function(arities)) =
411 program.function_schema(index as u16)
412 else {
413 continue;
414 };
415 let compatible = arities.iter().any(|schema| {
416 schema.fixed.len() == prototype.arity as usize
417 && schema.rest.is_some() == prototype.variadic
418 });
419 if !compatible {
420 return Err(CompileError::new(
421 CompileErrorKind::Arity,
422 format!(
423 "function schema for {} has no {}-argument arity{}",
424 prototype.name.as_deref().unwrap_or("<anonymous>"),
425 prototype.arity,
426 if prototype.variadic {
427 " with rest arguments"
428 } else {
429 ""
430 }
431 ),
432 None,
433 ));
434 }
435 }
436 Ok(())
437}
438
439fn top_level_operator(form: &Form, expected: &str) -> bool {
440 matches!(
441 crate::core::form_without_metadata(form),
442 Form::List(items)
443 if matches!(items.first(), Some(Form::Symbol(operator)) if operator == expected)
444 )
445}
446
447pub(crate) fn synthetic_spanned_form(form: Form) -> SpannedForm {
448 let position = Position {
449 offset: 0,
450 line: 1,
451 column: 1,
452 };
453 let children = match &form {
454 Form::List(values) | Form::Vector(values) | Form::Set(values) => {
455 values.iter().cloned().map(synthetic_spanned_form).collect()
456 }
457 Form::Map(entries) => entries
458 .iter()
459 .flat_map(|(key, value)| [key.clone(), value.clone()])
460 .map(synthetic_spanned_form)
461 .collect(),
462 Form::Tagged(_, value) | Form::Metadata(_, value) => {
463 vec![synthetic_spanned_form(value.as_ref().clone())]
464 }
465 _ => Vec::new(),
466 };
467 SpannedForm {
468 form,
469 span: Span {
470 start: position,
471 end: position,
472 },
473 children,
474 }
475}
476
477#[derive(Clone, Copy)]
480struct Child<'a> {
481 form: &'a Form,
482 span: &'a Span,
483 children: Option<&'a [SpannedForm]>,
484}
485
486struct FnContext {
489 proto_id: usize,
491 name: Option<String>,
492 params: u16,
494 variadic: bool,
497 suspend_allowed: bool,
500 async_function: bool,
502 captures: Vec<(String, Option<Position>)>,
505 code: Vec<Instruction>,
506 source_map: SourceMap,
507 scopes: ScopeStack,
508 loops: Vec<LoopContext>,
509 tries: Vec<TryContext>,
510 handlers: Vec<TryEntry>,
513 fallthrough: bool,
516}
517
518struct Compiler {
519 namespace: String,
522 constants: Vec<Value>,
523 constant_index: HashMap<Value, u32>,
524 functions: Vec<FunctionPrototype>,
525 contexts: Vec<FnContext>,
526 globals: Vec<String>,
530 excluded_foundation_libraries: HashSet<String>,
534 allow_unbound_globals: bool,
538 inline_globals: HashMap<String, String>,
541 var_metadata: Vec<std::rc::Rc<crate::lang::data::Metadata>>,
543 top_level: bool,
546 next_destructure_id: u64,
547}
548
549fn placeholder(
553 name: Option<String>,
554 arity: u16,
555 capture_count: u16,
556 variadic: bool,
557 async_function: bool,
558) -> FunctionPrototype {
559 FunctionPrototype {
560 name,
561 async_function,
562 arity,
563 variadic,
564 capture_count,
565 local_count: 0,
566 max_stack: 0,
567 code: Vec::new(),
568 source_map: SourceMap::default(),
569 handlers: Vec::new(),
570 }
571}
572
573impl Compiler {
574 fn predeclare_top_level(&mut self, forms: &[SpannedForm]) {
575 for spanned in forms {
576 let Form::List(items) = crate::core::form_without_metadata(&spanned.form) else {
577 continue;
578 };
579 let Some(Form::Symbol(operator)) = items.first() else {
580 continue;
581 };
582 if operator == "declare" {
583 for item in items.iter().skip(1) {
584 if let Form::Symbol(name) = crate::core::form_without_metadata(item) {
585 if !name.contains('/') {
586 self.declare_program_global(name);
587 }
588 }
589 }
590 continue;
591 }
592 }
593 }
594
595 fn new(
596 excluded_foundation_libraries: HashSet<String>,
597 allow_unbound_globals: bool,
598 ) -> Compiler {
599 let mut scopes = ScopeStack::new();
600 scopes.push_scope();
601 Compiler {
602 namespace: crate::core::namespace_registry()
603 .map(|registry| registry.current().name().as_str().to_owned())
604 .unwrap_or_else(|_| "user".into()),
605 constants: Vec::new(),
606 constant_index: HashMap::new(),
607 functions: vec![placeholder(None, 0, 0, false, false)],
608 contexts: vec![FnContext {
609 proto_id: 0,
610 name: None,
611 params: 0,
612 variadic: false,
613 suspend_allowed: false,
614 async_function: false,
615 captures: Vec::new(),
616 code: Vec::new(),
617 source_map: SourceMap::default(),
618 scopes,
619 loops: Vec::new(),
620 tries: Vec::new(),
621 handlers: Vec::new(),
622 fallthrough: true,
623 }],
624 globals: Vec::new(),
625 excluded_foundation_libraries,
626 allow_unbound_globals,
627 inline_globals: HashMap::new(),
628 var_metadata: Vec::new(),
629 top_level: true,
630 next_destructure_id: 0,
631 }
632 }
633
634 fn ctx(&self) -> &FnContext {
635 self.contexts.last().expect("function context is open")
636 }
637
638 fn ctx_mut(&mut self) -> &mut FnContext {
639 self.contexts.last_mut().expect("function context is open")
640 }
641
642 fn is_coroutine_var(&self, name: &str, member: &str) -> bool {
645 let canonical = format!("std.native.Coroutine/{member}");
646 let legacy = format!("std.foundation.coroutine/{member}");
647 if name == canonical || name == legacy {
648 return true;
649 }
650 crate::core::namespace_registry()
651 .ok()
652 .is_some_and(|registry| {
653 let Some(source) = registry.resolve(&crate::lang::data::Symbol::parse(name)) else {
654 return false;
655 };
656 [canonical, legacy].into_iter().any(|target| {
657 registry
658 .resolve(&crate::lang::data::Symbol::parse(&target))
659 .is_some_and(|target| source.same_identity(&target))
660 })
661 })
662 }
663
664 fn is_host_call_var(&self, name: &str) -> bool {
665 let canonical = "std.native.Host/call";
666 if name == canonical {
667 return true;
668 }
669 crate::core::namespace_registry()
670 .ok()
671 .and_then(|registry| {
672 let source = registry.resolve(&crate::lang::data::Symbol::parse(name))?;
673 let target = registry.resolve(&crate::lang::data::Symbol::parse(canonical))?;
674 Some(source.same_identity(&target))
675 })
676 .unwrap_or(false)
677 }
678
679 fn compile_host_call(
680 &mut self,
681 children: &[Child<'_>],
682 span: &Span,
683 ) -> Result<(), CompileError> {
684 if children.len() != 4 {
685 return Err(CompileError::new(
686 CompileErrorKind::Arity,
687 "std.native.Host/call expects service, method, and an argument vector",
688 Some(span.start),
689 ));
690 }
691 self.compile_call_arguments(children, span)?;
692 if self.ctx().fallthrough {
693 self.emit(Instruction::HostCall, Some(span.start));
694 }
695 Ok(())
696 }
697
698 fn children<'a>(&self, nodes: &'a [SpannedForm]) -> Vec<Child<'a>> {
702 nodes
703 .iter()
704 .map(|node| Child {
705 form: &node.form,
706 span: &node.span,
707 children: Some(&node.children),
708 })
709 .collect()
710 }
711
712 fn list_children<'a>(
713 &self,
714 elements: &'a [Form],
715 span: &'a Span,
716 children: Option<&'a [SpannedForm]>,
717 ) -> Vec<Child<'a>> {
718 let usable = children.filter(|nodes| nodes.len() == elements.len());
719 elements
720 .iter()
721 .enumerate()
722 .map(
723 |(index, form)| match usable.and_then(|nodes| nodes.get(index)) {
724 Some(node) => Child {
725 form: &node.form,
726 span: &node.span,
727 children: Some(&node.children),
728 },
729 None => Child {
730 form,
731 span,
732 children: None,
733 },
734 },
735 )
736 .collect()
737 }
738
739 fn emit(&mut self, instruction: Instruction, position: Option<Position>) -> usize {
740 let context = self.ctx_mut();
741 debug_assert!(context.fallthrough, "no emission after control terminates");
742 let index = context.code.len();
743 context.code.push(instruction);
744 context.source_map.record(position);
745 index
746 }
747
748 fn patch_jump(&mut self, at: usize, target: usize) {
749 let target = target as u32;
750 match &mut self.ctx_mut().code[at] {
751 Instruction::Jump(operand) | Instruction::JumpIfFalse(operand) => *operand = target,
752 other => unreachable!("patching non-jump instruction: {other:?}"),
753 }
754 }
755
756 fn constant(&mut self, value: Value, span: &Span) -> Result<(), CompileError> {
757 let index = self.constant_index_of(value, span)?;
758 self.emit(Instruction::Constant(index), Some(span.start));
759 Ok(())
760 }
761
762 fn unique_constant(&mut self, value: Value, span: &Span) -> Result<(), CompileError> {
763 if self.constants.len() >= MAX_CONSTANTS {
764 return Err(CompileError::new(
765 CompileErrorKind::Limit,
766 format!("constant pool exceeds limit of {MAX_CONSTANTS}"),
767 Some(span.start),
768 ));
769 }
770 let index = self.constants.len() as u32;
771 self.constants.push(value);
772 self.emit(Instruction::Constant(index), Some(span.start));
773 Ok(())
774 }
775
776 fn constant_index_of(&mut self, value: Value, span: &Span) -> Result<u32, CompileError> {
780 match self.constant_index.get(&value) {
781 Some(index) => Ok(*index),
782 None => {
783 if self.constants.len() >= MAX_CONSTANTS {
784 return Err(CompileError::new(
785 CompileErrorKind::Limit,
786 format!("constant pool exceeds limit of {MAX_CONSTANTS}"),
787 Some(span.start),
788 ));
789 }
790 let index = self.constants.len() as u32;
791 self.constants.push(value.clone());
792 self.constant_index.insert(value, index);
793 Ok(index)
794 }
795 }
796 }
797
798 fn unsupported(&self, form: &Form, span: &Span) -> CompileError {
799 let message = match form {
800 Form::List(elements) => match elements.first() {
801 Some(Form::Symbol(name)) => format!("unsupported operator: {name}"),
802 _ => format!("unsupported form: {form}"),
803 },
804 _ => format!("unsupported form: {form}"),
805 };
806 CompileError::new(CompileErrorKind::UnsupportedForm, message, Some(span.start))
807 }
808
809 fn compile_sequence(&mut self, children: &[Child<'_>], tail: bool) -> Result<(), CompileError> {
813 if children.is_empty() {
814 self.emit(Instruction::Nil, None);
815 return Ok(());
816 }
817 let top = self.top_level && self.contexts.len() == 1;
818 let last = children.len() - 1;
819 for (index, child) in children.iter().enumerate() {
820 if !self.ctx().fallthrough {
821 break;
822 }
823 self.top_level = top;
824 self.compile_form(
825 child.form,
826 child.span,
827 child.children,
828 tail && index == last,
829 )?;
830 if index != last && self.ctx().fallthrough {
831 self.emit(Instruction::Pop, Some(child.span.start));
832 }
833 }
834 Ok(())
835 }
836
837 fn compile_form(
838 &mut self,
839 form: &Form,
840 span: &Span,
841 children: Option<&[SpannedForm]>,
842 tail: bool,
843 ) -> Result<(), CompileError> {
844 let top = self.top_level;
845 self.top_level = false;
846 if !self.ctx().fallthrough {
847 return Ok(());
850 }
851 if let Some(expanded) =
852 destructure::expand(form, &mut self.next_destructure_id).map_err(|message| {
853 CompileError::new(CompileErrorKind::UnsupportedForm, message, Some(span.start))
854 })?
855 {
856 self.top_level = top;
857 return self.compile_form(&expanded, span, None, tail);
858 }
859 if let Form::List(values) = crate::core::form_without_metadata(form) {
860 let protected = matches!(
861 values.first(),
862 Some(Form::Symbol(name))
863 if name == "quote" || name == "syntax-quote" || name == "comment"
864 );
865 if !protected {
866 let expanded = crate::core::vm_macroexpand(form).map_err(|message| {
867 CompileError::new(CompileErrorKind::UnsupportedForm, message, Some(span.start))
868 })?;
869 if expanded != *form {
870 self.top_level = top;
871 return self.compile_form(&expanded, span, None, tail);
872 }
873 }
874 }
875 match form {
876 Form::Nil => {
877 self.emit(Instruction::Nil, Some(span.start));
878 Ok(())
879 }
880 Form::Bool(true) => {
881 self.emit(Instruction::True, Some(span.start));
882 Ok(())
883 }
884 Form::Bool(false) => {
885 self.emit(Instruction::False, Some(span.start));
886 Ok(())
887 }
888 Form::Number(value) => self.constant(Value::Number(*value), span),
889 Form::Float(value) => {
890 let value = crate::numeric::finite_float(*value).map_err(|error| {
891 CompileError::new(CompileErrorKind::Parse, error, Some(span.start))
892 })?;
893 self.constant(Value::Float(value), span)
894 }
895 Form::String(value) => self.constant(Value::String(value.clone()), span),
896 Form::Keyword(value) => self.constant(Value::Keyword(value.clone().into()), span),
897 Form::Character(value) => self.constant(Value::Character(*value), span),
898 Form::BigInteger(value) => {
899 self.constant(crate::numeric::compact_integer(value.clone()), span)
900 }
901 Form::Regex(value) => self.constant(Value::Regex(value.clone()), span),
902 Form::Tagged(tag, value) if tag == "uuid" => {
903 let Form::String(text) = value.as_ref() else {
904 return Err(CompileError::new(
905 CompileErrorKind::UnsupportedForm,
906 "#uuid expects a UUID string literal",
907 Some(span.start),
908 ));
909 };
910 let value = crate::core::uuid_tag_value(Value::String(text.clone())).map_err(
911 |message| CompileError::new(CompileErrorKind::Parse, message, Some(span.start)),
912 )?;
913 self.constant(value, span)
914 }
915 Form::Tagged(tag, value) if tag == "arr" => {
916 let Form::Vector(values) = value.as_ref() else {
917 return Err(CompileError::new(
918 CompileErrorKind::UnsupportedForm,
919 "#arr expects a vector literal",
920 Some(span.start),
921 ));
922 };
923 if values.len() > MAX_PRIMITIVE_ARGUMENTS {
924 return Err(CompileError::new(
925 CompileErrorKind::Limit,
926 format!(
927 "#arr supports at most {MAX_PRIMITIVE_ARGUMENTS} elements"
928 ),
929 Some(span.start),
930 ));
931 }
932 self.compile_collection_values(values.iter(), span)?;
933 let target = self.name_constant("std.native.Arr/new", span)?;
934 self.emit(
935 Instruction::IntrinsicCall {
936 target,
937 argc: values.len() as u8,
938 },
939 Some(span.start),
940 );
941 Ok(())
942 }
943 Form::Tagged(tag, value) if tag == "obj" => {
944 let Form::Map(entries) = value.as_ref() else {
945 return Err(CompileError::new(
946 CompileErrorKind::UnsupportedForm,
947 "#obj expects a map literal",
948 Some(span.start),
949 ));
950 };
951 let arguments = entries.len().saturating_mul(2);
952 if arguments > MAX_PRIMITIVE_ARGUMENTS {
953 return Err(CompileError::new(
954 CompileErrorKind::Limit,
955 format!(
956 "#obj supports at most {} entries",
957 MAX_PRIMITIVE_ARGUMENTS / 2
958 ),
959 Some(span.start),
960 ));
961 }
962 for (key, value) in entries {
963 self.compile_form(key, span, None, false)?;
964 self.compile_form(value, span, None, false)?;
965 }
966 let target = self.name_constant("std.native.Obj/new", span)?;
967 self.emit(
968 Instruction::IntrinsicCall {
969 target,
970 argc: arguments as u8,
971 },
972 Some(span.start),
973 );
974 Ok(())
975 }
976 Form::Vector(values) => {
984 if values.iter().all(literal_collection_form) {
985 let value = crate::core::form_to_value(form).map_err(|message| {
986 CompileError::new(
987 CompileErrorKind::UnsupportedForm,
988 message,
989 Some(span.start),
990 )
991 })?;
992 return self.unique_constant(value, span);
993 }
994 self.compile_collection_values(values.iter(), span)?;
995 self.emit(
996 Instruction::BuildVector(self.collection_count(values.len(), span)?),
997 Some(span.start),
998 );
999 Ok(())
1000 }
1001 Form::Map(entries) => {
1002 if entries.len() > usize::from(u16::MAX) {
1006 return Err(CompileError::new(
1007 CompileErrorKind::Limit,
1008 "map literal exceeds 65535 entries",
1009 Some(span.start),
1010 ));
1011 }
1012 for (key, value) in entries {
1013 self.compile_form(key, span, None, false)?;
1014 self.compile_form(value, span, None, false)?;
1015 }
1016 self.emit(
1017 Instruction::BuildMap(entries.len() as u16),
1018 Some(span.start),
1019 );
1020 Ok(())
1021 }
1022 Form::Set(values) => {
1023 self.compile_collection_values(values.iter(), span)?;
1024 self.emit(
1025 Instruction::BuildSet(self.collection_count(values.len(), span)?),
1026 Some(span.start),
1027 );
1028 Ok(())
1029 }
1030 Form::Metadata(_, value) => {
1031 self.top_level = top;
1036 self.compile_form(value, span, None, tail)
1037 }
1038 Form::Symbol(name) => match self.ctx().scopes.resolve(name) {
1039 Some(slot) => {
1040 self.emit(Instruction::LoadLocal(slot), Some(span.start));
1041 Ok(())
1042 }
1043 None if self.visible_global(name) => self.emit_get_global(name, span),
1044 None if IntrinsicOp::from_symbol(name).is_some() => {
1045 let target = self.name_constant(name, span)?;
1046 self.emit(Instruction::IntrinsicValue(target), Some(span.start));
1047 Ok(())
1048 }
1049 None if self.visible_bytecode_callable(name) => {
1050 let index = self.name_constant(name, span)?;
1051 self.emit(Instruction::BuiltinValue(index), Some(span.start));
1052 Ok(())
1053 }
1054 None if self.visible_namespace(name) => {
1055 let index = self.name_constant(name, span)?;
1056 self.emit(Instruction::NamespaceValue(index), Some(span.start));
1057 Ok(())
1058 }
1059 None => Err(CompileError::new(
1060 CompileErrorKind::UnboundSymbol,
1061 format!("unbound symbol: {name}"),
1062 Some(span.start),
1063 )),
1064 },
1065 Form::List(elements) if elements.is_empty() => {
1066 self.constant(Value::List(PList::new()), span)
1067 }
1068 Form::List(elements) => {
1069 let children = self.list_children(elements, span, children);
1070 match &elements[0] {
1071 Form::Symbol(name) if self.is_coroutine_var(name, "await") => {
1072 self.compile_await(&children, span)
1073 }
1074 Form::Symbol(name) if self.is_coroutine_var(name, "yield") => {
1075 self.compile_yield(&children, span)
1076 }
1077 Form::Symbol(name) if self.is_host_call_var(name) => {
1078 self.compile_host_call(&children, span)
1079 }
1080 Form::Symbol(name) if name == "." => {
1081 if elements.len() != 3 {
1082 return Err(CompileError::new(
1083 CompileErrorKind::UnsupportedForm,
1084 "dot expects a receiver and method",
1085 Some(span.start),
1086 ));
1087 }
1088 let Form::List(method_form) = &elements[2] else {
1089 return Err(CompileError::new(
1090 CompileErrorKind::UnsupportedForm,
1091 "dot call expects a method list",
1092 Some(span.start),
1093 ));
1094 };
1095 let Some(Form::Symbol(method_name)) = method_form.first() else {
1096 return Err(CompileError::new(
1097 CompileErrorKind::UnsupportedForm,
1098 "dot method must be a symbol",
1099 Some(span.start),
1100 ));
1101 };
1102 self.compile_form(&elements[1], span, None, false)?;
1103 for argument in &method_form[1..] {
1104 self.compile_form(argument, span, None, false)?;
1105 }
1106 let method = self.name_constant(method_name, span)?;
1107 self.emit(
1108 Instruction::DotCall {
1109 method,
1110 argc: (method_form.len() - 1) as u8,
1111 },
1112 Some(span.start),
1113 );
1114 Ok(())
1115 }
1116 Form::Symbol(name) if name == "if" => self.compile_if(&children, span, tail),
1117 Form::Symbol(name) if name == "and" => self.compile_and(&children, span, tail),
1118 Form::Symbol(name) if name == "or" => self.compile_or(&children, span, tail),
1119 Form::Symbol(name) if name == "cond" => {
1120 self.compile_cond(&children, span, tail)
1121 }
1122 Form::Symbol(name) if name == "quote" => self.compile_quote(&children, span),
1123 Form::Symbol(name) if name == "comment" => {
1124 self.emit(Instruction::Nil, Some(span.start));
1125 Ok(())
1126 }
1127 Form::Symbol(name) if name == "syntax-quote" => {
1128 self.compile_syntax_quote(&children, span)
1129 }
1130 Form::Symbol(name) if name == "do" => {
1131 self.top_level = top;
1135 self.compile_sequence(&children[1..], tail)
1136 }
1137 Form::Symbol(name) if name == "let" => self.compile_let(&children, span, tail),
1138 Form::Symbol(name) if name == "loop" => {
1139 self.compile_loop(&children, span, tail)
1140 }
1141 Form::Symbol(name) if name == "recur" => {
1142 self.compile_recur(&children, span, tail)
1143 }
1144 Form::Symbol(name) if name == "fn" => self.compile_fn_form(&children, span),
1145 Form::Symbol(name) if name == "def" => self.compile_def(&children, span),
1146 Form::Symbol(name) if name == "defn" => self.compile_defn(&children, span, top),
1147 Form::Symbol(name) if name == "defmacro" => {
1148 self.compile_defmacro(&children, span, top)
1149 }
1150 Form::Symbol(name) if name == "declare" => {
1151 self.compile_declare(&children, span, top)
1152 }
1153 Form::Symbol(name) if name == "var" => self.compile_var(&children, span),
1154 Form::Symbol(name) if name == "set!" => self.compile_set(&children, span),
1155 Form::Symbol(name)
1156 if name == "require" || (matches!(name.as_str(), "ns" | "ns+") && !top) =>
1157 {
1158 let value = crate::core::form_to_value(form).map_err(|message| {
1159 CompileError::new(
1160 CompileErrorKind::UnsupportedForm,
1161 message,
1162 Some(span.start),
1163 )
1164 })?;
1165 let index = self.constant_index_of(value, span)?;
1166 self.emit(Instruction::NamespaceOperation(index), Some(span.start));
1167 Ok(())
1168 }
1169 Form::Symbol(name) if name == "ns" || name == "ns+" => {
1170 if !top {
1171 return Err(self.unsupported(form, span));
1172 }
1173 self.emit(Instruction::Nil, Some(span.start));
1174 Ok(())
1175 }
1176 Form::Symbol(name) if name == "field" => self.compile_field(&children, span),
1177 Form::Symbol(name) if name == "instance?" => {
1178 self.compile_instance_of(&children, span)
1179 }
1180 Form::Symbol(name) if name == "try" => self.compile_try(&children, span, tail),
1181 Form::Symbol(name) if name == "__dynamic-bind" => {
1182 self.compile_dynamic_binding(&children, span, true)
1183 }
1184 Form::Symbol(name) if name == "__dynamic-unbind" => {
1185 self.compile_dynamic_binding(&children, span, false)
1186 }
1187 Form::Symbol(name) if name == "throw" => self.compile_throw(&children, span),
1188 Form::Symbol(name)
1189 if UNSUPPORTED_OPERATORS.contains(&name.as_str())
1190 && self.ctx().scopes.resolve(name).is_none()
1191 && !self.visible_global(name) =>
1192 {
1193 Err(self.unsupported(form, span))
1194 }
1195 Form::Symbol(name)
1196 if (name.starts_with("std.native.Arr/")
1197 || name.starts_with("std.native.Obj/"))
1198 && IntrinsicOp::from_symbol(name).is_some() =>
1199 {
1200 self.compile_primitive(
1201 &children,
1202 span,
1203 IntrinsicOp::from_symbol(name).expect("intrinsic was checked"),
1204 )
1205 }
1206 Form::Symbol(name)
1213 if self.ctx().scopes.resolve(name).is_some()
1214 || self.visible_global(name) =>
1215 {
1216 self.compile_named_call(name, &children, span)
1217 }
1218 Form::Symbol(name) => match IntrinsicOp::from_symbol(name) {
1219 Some(op) => self.compile_primitive(&children, span, op),
1220 None => self.compile_named_call(name, &children, span),
1221 },
1222 _ => self.compile_expression_call(&children, span),
1223 }
1224 }
1225 _ => Err(self.unsupported(form, span)),
1226 }
1227 }
1228
1229 fn collection_count(&self, count: usize, span: &Span) -> Result<u16, CompileError> {
1230 u16::try_from(count).map_err(|_| {
1231 CompileError::new(
1232 CompileErrorKind::Limit,
1233 "collection literal exceeds 65535 items",
1234 Some(span.start),
1235 )
1236 })
1237 }
1238
1239 fn compile_collection_values<'a>(
1240 &mut self,
1241 values: impl Iterator<Item = &'a Form>,
1242 span: &Span,
1243 ) -> Result<(), CompileError> {
1244 for value in values {
1245 self.compile_form(value, span, None, false)?;
1246 }
1247 Ok(())
1248 }
1249
1250 fn compile_call_arguments(
1252 &mut self,
1253 children: &[Child<'_>],
1254 span: &Span,
1255 ) -> Result<(), CompileError> {
1256 let argc = children.len() - 1;
1257 if argc > MAX_PRIMITIVE_ARGUMENTS {
1258 return Err(CompileError::new(
1259 CompileErrorKind::Limit,
1260 format!("calls support at most {MAX_PRIMITIVE_ARGUMENTS} arguments"),
1261 Some(span.start),
1262 ));
1263 }
1264 for argument in &children[1..] {
1265 self.compile_form(argument.form, argument.span, argument.children, false)?;
1266 }
1267 Ok(())
1268 }
1269
1270 fn compile_if(
1271 &mut self,
1272 children: &[Child<'_>],
1273 span: &Span,
1274 tail: bool,
1275 ) -> Result<(), CompileError> {
1276 if children.len() != 3 && children.len() != 4 {
1279 return Err(CompileError::new(
1280 CompileErrorKind::Arity,
1281 "if expects 2 or 3 arguments",
1282 Some(span.start),
1283 ));
1284 }
1285 let condition = &children[1];
1286 self.compile_form(condition.form, condition.span, condition.children, false)?;
1287 if !self.ctx().fallthrough {
1288 return Ok(());
1291 }
1292 let jump_else = self.emit(Instruction::JumpIfFalse(0), Some(condition.span.start));
1293 let then = &children[2];
1294 self.compile_form(then.form, then.span, then.children, tail)?;
1295 let then_fell = self.ctx().fallthrough;
1296 let jump_end = if then_fell {
1297 Some(self.emit(Instruction::Jump(0), Some(then.span.start)))
1298 } else {
1299 None
1300 };
1301 self.ctx_mut().fallthrough = true;
1303 let else_target = self.ctx().code.len();
1304 if let Some(else_form) = children.get(3) {
1305 self.compile_form(else_form.form, else_form.span, else_form.children, tail)?;
1306 } else {
1307 self.emit(Instruction::Nil, Some(span.start));
1308 }
1309 let else_fell = self.ctx().fallthrough;
1310 let end = self.ctx().code.len();
1311 self.patch_jump(jump_else, else_target);
1312 if let Some(jump_end) = jump_end {
1313 self.patch_jump(jump_end, end);
1314 }
1315 self.ctx_mut().fallthrough = then_fell || else_fell;
1316 Ok(())
1317 }
1318
1319 fn compile_and(
1320 &mut self,
1321 children: &[Child<'_>],
1322 span: &Span,
1323 tail: bool,
1324 ) -> Result<(), CompileError> {
1325 if children.len() == 1 {
1326 self.emit(Instruction::True, Some(span.start));
1327 return Ok(());
1328 }
1329 let mut false_jumps = Vec::new();
1330 for child in &children[1..children.len() - 1] {
1331 self.compile_form(child.form, child.span, child.children, false)?;
1332 if !self.ctx().fallthrough {
1333 break;
1334 }
1335 self.emit(Instruction::Dup, Some(child.span.start));
1336 false_jumps.push(self.emit(Instruction::JumpIfFalse(0), Some(child.span.start)));
1337 self.emit(Instruction::Pop, Some(child.span.start));
1338 }
1339 if self.ctx().fallthrough {
1340 let last = children.last().expect("and has an argument");
1341 self.compile_form(last.form, last.span, last.children, tail)?;
1342 }
1343 let last_fell = self.ctx().fallthrough;
1344 let end = self.ctx().code.len();
1345 let short_circuits = !false_jumps.is_empty();
1346 for jump in false_jumps {
1347 self.patch_jump(jump, end);
1348 }
1349 self.ctx_mut().fallthrough = last_fell || short_circuits;
1350 Ok(())
1351 }
1352
1353 fn compile_or(
1354 &mut self,
1355 children: &[Child<'_>],
1356 span: &Span,
1357 tail: bool,
1358 ) -> Result<(), CompileError> {
1359 if children.len() == 1 {
1360 self.emit(Instruction::Nil, Some(span.start));
1361 return Ok(());
1362 }
1363 let mut end_jumps = Vec::new();
1364 for child in &children[1..children.len() - 1] {
1365 self.compile_form(child.form, child.span, child.children, false)?;
1366 if !self.ctx().fallthrough {
1367 break;
1368 }
1369 self.emit(Instruction::Dup, Some(child.span.start));
1370 let false_jump = self.emit(Instruction::JumpIfFalse(0), Some(child.span.start));
1371 end_jumps.push(self.emit(Instruction::Jump(0), Some(child.span.start)));
1372 let next = self.ctx().code.len();
1373 self.patch_jump(false_jump, next);
1374 self.emit(Instruction::Pop, Some(child.span.start));
1375 }
1376 if self.ctx().fallthrough {
1377 let last = children.last().expect("or has an argument");
1378 self.compile_form(last.form, last.span, last.children, tail)?;
1379 }
1380 let last_fell = self.ctx().fallthrough;
1381 let end = self.ctx().code.len();
1382 let short_circuits = !end_jumps.is_empty();
1383 for jump in end_jumps {
1384 self.patch_jump(jump, end);
1385 }
1386 self.ctx_mut().fallthrough = last_fell || short_circuits;
1387 Ok(())
1388 }
1389
1390 fn compile_cond(
1391 &mut self,
1392 children: &[Child<'_>],
1393 span: &Span,
1394 tail: bool,
1395 ) -> Result<(), CompileError> {
1396 let clauses = &children[1..];
1397 if clauses.len() % 2 != 0 {
1398 return Err(CompileError::new(
1399 CompileErrorKind::Arity,
1400 "cond expects test/expression pairs",
1401 Some(span.start),
1402 ));
1403 }
1404 if clauses.is_empty() {
1405 self.emit(Instruction::Nil, Some(span.start));
1406 return Ok(());
1407 }
1408 let mut end_jumps = Vec::new();
1409 for pair in clauses.chunks(2) {
1410 self.compile_form(pair[0].form, pair[0].span, pair[0].children, false)?;
1411 if !self.ctx().fallthrough {
1412 let end = self.ctx().code.len();
1413 for jump in end_jumps {
1414 self.patch_jump(jump, end);
1415 }
1416 return Ok(());
1417 }
1418 let next_jump = self.emit(Instruction::JumpIfFalse(0), Some(pair[0].span.start));
1419 self.compile_form(pair[1].form, pair[1].span, pair[1].children, tail)?;
1420 if self.ctx().fallthrough {
1421 end_jumps.push(self.emit(Instruction::Jump(0), Some(pair[1].span.start)));
1422 }
1423 self.ctx_mut().fallthrough = true;
1426 let next = self.ctx().code.len();
1427 self.patch_jump(next_jump, next);
1428 }
1429 self.emit(Instruction::Nil, Some(span.start));
1430 let end = self.ctx().code.len();
1431 for jump in end_jumps {
1432 self.patch_jump(jump, end);
1433 }
1434 Ok(())
1435 }
1436
1437 fn compile_dynamic_binding(
1438 &mut self,
1439 children: &[Child<'_>],
1440 span: &Span,
1441 bind: bool,
1442 ) -> Result<(), CompileError> {
1443 let expected = if bind { 3 } else { 2 };
1444 if children.len() != expected {
1445 return Err(CompileError::new(
1446 CompileErrorKind::Arity,
1447 if bind {
1448 "dynamic bind expects a Var and value"
1449 } else {
1450 "dynamic unbind expects a Var"
1451 },
1452 Some(span.start),
1453 ));
1454 }
1455 let Form::Symbol(name) = children[1].form else {
1456 return Err(CompileError::new(
1457 CompileErrorKind::UnsupportedForm,
1458 "dynamic binding target must be a symbol",
1459 Some(children[1].span.start),
1460 ));
1461 };
1462 let name = self.global_name_constant(name, children[1].span)?;
1463 if bind {
1464 self.compile_form(
1465 children[2].form,
1466 children[2].span,
1467 children[2].children,
1468 false,
1469 )?;
1470 if self.ctx().fallthrough {
1471 self.emit(Instruction::DynamicBind(name), Some(span.start));
1472 }
1473 } else {
1474 self.emit(Instruction::DynamicUnbind(name), Some(span.start));
1475 }
1476 Ok(())
1477 }
1478
1479 fn compile_bindings(
1482 &mut self,
1483 children: &[Child<'_>],
1484 form_name: &str,
1485 ) -> Result<Vec<u16>, CompileError> {
1486 let bindings = &children[1];
1487 let pairs: &[Form] = match bindings.form {
1488 Form::List(values) | Form::Vector(values) => values,
1489 _ => {
1490 return Err(CompileError::new(
1491 CompileErrorKind::Arity,
1492 format!("{form_name} expects a binding list or vector"),
1493 Some(bindings.span.start),
1494 ))
1495 }
1496 };
1497 if pairs.len() % 2 != 0 {
1498 return Err(CompileError::new(
1499 CompileErrorKind::Arity,
1500 format!("{form_name} bindings require name/value pairs"),
1501 Some(bindings.span.start),
1502 ));
1503 }
1504 let pair_children = self.list_children(pairs, bindings.span, bindings.children);
1506 let mut slots = Vec::with_capacity(pairs.len() / 2);
1507 for pair in pair_children.chunks(2) {
1508 let (name, initializer) = (&pair[0], &pair[1]);
1509 let Form::Symbol(symbol) = name.form else {
1512 return Err(CompileError::new(
1513 CompileErrorKind::UnsupportedForm,
1514 format!("{form_name} destructuring is not supported"),
1515 Some(name.span.start),
1516 ));
1517 };
1518 self.compile_form(
1519 initializer.form,
1520 initializer.span,
1521 initializer.children,
1522 false,
1523 )?;
1524 if !self.ctx().fallthrough {
1525 return Ok(slots);
1526 }
1527 let slot = self.ctx_mut().scopes.declare(symbol).map_err(|error| {
1528 CompileError::new(error.kind(), error.message(), Some(name.span.start))
1529 })?;
1530 self.emit(Instruction::StoreLocal(slot), Some(name.span.start));
1531 slots.push(slot);
1532 }
1533 Ok(slots)
1534 }
1535
1536 fn compile_let(
1537 &mut self,
1538 children: &[Child<'_>],
1539 span: &Span,
1540 tail: bool,
1541 ) -> Result<(), CompileError> {
1542 if children.len() < 3 {
1543 return Err(CompileError::new(
1544 CompileErrorKind::Arity,
1545 "let expects bindings and a body",
1546 Some(span.start),
1547 ));
1548 }
1549 self.ctx_mut().scopes.push_scope();
1550 let result = self
1551 .compile_bindings(children, "let")
1552 .and_then(|_| self.compile_sequence(&children[2..], tail));
1553 self.ctx_mut().scopes.pop_scope();
1554 result
1555 }
1556
1557 fn compile_loop(
1558 &mut self,
1559 children: &[Child<'_>],
1560 span: &Span,
1561 _tail: bool,
1562 ) -> Result<(), CompileError> {
1563 if children.len() < 3 {
1564 return Err(CompileError::new(
1565 CompileErrorKind::Arity,
1566 "loop expects bindings and a body",
1567 Some(span.start),
1568 ));
1569 }
1570 self.ctx_mut().scopes.push_scope();
1571 let result = self.compile_bindings(children, "loop").and_then(|slots| {
1572 let header = self.ctx().code.len();
1573 self.ctx_mut().loops.push(LoopContext { header, slots });
1574 let result = self.compile_sequence(&children[2..], true);
1577 self.ctx_mut().loops.pop();
1578 result
1579 });
1580 self.ctx_mut().scopes.pop_scope();
1581 result
1582 }
1583
1584 fn finish(mut self) -> Result<Program, CompileError> {
1585 if self.ctx().fallthrough {
1586 self.emit(Instruction::Return, None);
1587 }
1588 self.close_context();
1589 let mut program = Program {
1590 namespace: None,
1591 var_metadata: self.var_metadata,
1592 schema_types: HashMap::new(),
1593 function_types: HashMap::new(),
1594 inferred_function_types: HashMap::new(),
1595 constants: self.constants,
1596 functions: self.functions,
1597 entry: 0,
1598 };
1599 for index in 0..program.functions.len() {
1603 let heights = stack_heights(&program, &program.functions[index])
1604 .map_err(|error| internal(error.to_string()))?;
1605 program.functions[index].max_stack = heights.iter().copied().max().unwrap_or(0);
1606 for entry_index in 0..program.functions[index].handlers.len() {
1607 let start = program.functions[index].handlers[entry_index].start as usize;
1608 program.functions[index].handlers[entry_index].depth = heights[start];
1609 }
1610 }
1611 validate::validate(&program).map_err(|error| internal(error.to_string()))?;
1612 Ok(program)
1613 }
1614}
1615
1616fn literal_collection_form(form: &Form) -> bool {
1617 match form {
1618 Form::Nil
1619 | Form::Bool(_)
1620 | Form::Number(_)
1621 | Form::Float(_)
1622 | Form::String(_)
1623 | Form::Keyword(_)
1624 | Form::Character(_)
1625 | Form::BigInteger(_)
1626 | Form::Regex(_) => true,
1627 Form::Vector(values) | Form::Set(values) => values.iter().all(literal_collection_form),
1628 Form::Map(entries) => entries
1629 .iter()
1630 .all(|(key, value)| literal_collection_form(key) && literal_collection_form(value)),
1631 _ => false,
1632 }
1633}
1634
1635fn constant_form(form: &Form) -> bool {
1636 match form {
1637 Form::Nil
1638 | Form::Bool(_)
1639 | Form::Number(_)
1640 | Form::Float(_)
1641 | Form::BigInteger(_)
1642 | Form::Character(_)
1643 | Form::Regex(_)
1644 | Form::Keyword(_)
1645 | Form::String(_) => true,
1646 Form::Tagged(_, value) | Form::Metadata(_, value) => constant_form(value),
1647 Form::Vector(values) | Form::Set(values) => values.iter().all(constant_form),
1648 Form::Map(entries) => entries
1649 .iter()
1650 .all(|(key, value)| constant_form(key) && constant_form(value)),
1651 Form::Symbol(_) | Form::List(_) => false,
1652 }
1653}
1654
1655fn unquote_argument(form: &Form, operator: &str) -> Option<Result<Form, String>> {
1656 let Form::List(parts) = crate::core::form_without_metadata(form) else {
1657 return None;
1658 };
1659 if !matches!(parts.first(), Some(Form::Symbol(name)) if name == operator) {
1660 return None;
1661 }
1662 Some(if parts.len() == 2 {
1663 Ok(parts[1].clone())
1664 } else {
1665 Err(format!("{operator} expects one argument"))
1666 })
1667}
1668
1669fn internal(message: String) -> CompileError {
1670 CompileError::new(CompileErrorKind::Internal, message, None)
1671}
1672
1673#[cfg(test)]
1674#[path = "compiler/tests.rs"]
1675mod tests;