1use std::{collections::HashSet, rc::Rc};
15
16use adam_rs::{CellKind, RelationshipSlot};
17use cel_parser::{Expr, OpLookup};
18use cel_runtime::{DynSegment, ValueType};
19
20use super::{
21 analysis::{self, Body, DefinitionPlan, Kind},
22 *,
23};
24use crate::{
25 CompositionBuilder, TypeRegistry,
26 cell_value::{CellValue, RawFrame},
27 composition::{AuthoredItem, AuthoredRole, CellSlot, MethodRecipe},
28 type_registry::TypeShape,
29};
30
31pub(crate) struct Environment {
33 pub(crate) types: Rc<TypeRegistry>,
34 pub(crate) lookup: OpLookup,
35}
36
37pub struct ModuleCompiler {
42 environment: Rc<Environment>,
43}
44
45impl ModuleCompiler {
46 pub fn new(types: TypeRegistry, lookup: OpLookup) -> Self {
59 Self {
60 environment: Rc::new(Environment {
61 types: Rc::new(types),
62 lookup,
63 }),
64 }
65 }
66
67 pub fn compile(self, modules: ModuleSet) -> Result<CompiledModules, Vec<ModuleDiagnostic>> {
81 compile(self.environment, modules)
82 }
83}
84
85#[derive(Clone, Debug)]
90pub struct RootCell {
91 pub name: String,
93 pub kind: CellKind,
95 pub shape: TypeShape,
97 pub slot: adam_rs::CellSlot,
99 pub label: SourceLabel,
101}
102
103pub struct PreparedRoot {
108 pub(crate) template: TypedTemplate,
109 cells: Vec<RootCell>,
110}
111
112impl PreparedRoot {
113 pub fn cells(&self) -> &[RootCell] {
119 &self.cells
120 }
121
122 pub fn definition_label(&self) -> &SourceLabel {
127 self.template.definition_label()
128 }
129}
130
131pub struct CompiledModules {
137 templates: HashMap<ModulePath, HashMap<String, TypedTemplate>>,
138 roots: HashMap<ModulePath, Vec<(String, PreparedRoot)>>,
139 sources: Vec<SourceId>,
140 pub(crate) environment: Rc<Environment>,
142}
143
144impl CompiledModules {
145 pub fn instantiate_root(
169 &self,
170 module: &ModulePath,
171 name: &str,
172 ) -> Result<crate::ParsedSheet, ModuleDiagnostic> {
173 let root = self.prepared_root(module, name).ok_or_else(|| {
174 crate::composition::diagnostic(format!("unknown root `{module}::{name}`"))
175 })?;
176 let mut destination = crate::ParsedSheet::new();
177 let instance = root.template.instantiate(&mut destination, &[])?;
178 let occurrence = instance.occurrences()[0].id();
179 for cell in root.cells() {
180 let id = instance
181 .cell(occurrence, &cell.slot)
182 .expect("prepared root owns each authored local slot");
183 destination
184 .cell_names
185 .insert(cell.name.clone(), (id, cell.shape.clone()));
186 if cell.kind == adam_rs::CellKind::Out {
187 destination.output_names.insert(cell.name.clone(), id);
188 }
189 }
190 destination.retain_root_spans(&instance);
191 let mut sources: HashSet<_> = destination.sources.iter().cloned().collect();
192 for source in &self.sources {
193 if sources.insert(source.clone()) {
194 destination.sources.push(source.clone());
195 }
196 }
197 destination.sheet.validate().map_err(|error| {
198 destination.validation_diagnostic(error, root.definition_label(), &instance)
199 })?;
200 Ok(destination)
201 }
202
203 pub fn sources(&self) -> &[SourceId] {
217 &self.sources
218 }
219
220 pub fn compiler(&self) -> ModuleCompiler {
233 ModuleCompiler {
234 environment: self.environment.clone(),
235 }
236 }
237
238 pub fn template(&self, module: &ModulePath, name: &str) -> Option<&TypedTemplate> {
244 self.templates.get(module)?.get(name)
245 }
246
247 pub fn roots<'a>(&'a self, module: &ModulePath) -> impl Iterator<Item = &'a str> {
254 self.roots
255 .get(module)
256 .into_iter()
257 .flatten()
258 .map(|(name, _)| name.as_str())
259 }
260
261 pub fn prepared_root(&self, module: &ModulePath, name: &str) -> Option<&PreparedRoot> {
277 self.roots
278 .get(module)?
279 .iter()
280 .find(|(root, _)| root == name)
281 .map(|(_, root)| root)
282 }
283}
284
285struct Expression {
287 syntax: Expr,
288 inputs: Vec<(String, ValueType)>,
289 candidate: Option<ValueType>,
290 descriptor: ValueType,
291 label: SourceLabel,
292 environment: Rc<Environment>,
293}
294
295impl Expression {
296 fn own(
300 expression: &analysis::Expression<'_>,
301 plan: &DefinitionPlan<'_>,
302 environment: &Rc<Environment>,
303 ) -> Self {
304 Self {
305 syntax: expression.syntax.clone(),
306 inputs: expression
307 .inputs
308 .iter()
309 .map(|id| {
310 let cell = &plan.cells[*id];
311 (
312 cell.name.to_owned(),
313 environment.types.value_type(&cell.shape),
314 )
315 })
316 .collect(),
317 candidate: expression
318 .candidate
319 .map(|id| environment.types.value_type(&plan.cells[id].shape)),
320 descriptor: expression.descriptor.clone(),
321 label: expression.label.clone(),
322 environment: environment.clone(),
323 }
324 }
325
326 fn emit(&self) -> anyhow::Result<DynSegment> {
332 analysis::Emission {
333 syntax: &self.syntax,
334 inputs: self
335 .inputs
336 .iter()
337 .map(|(name, descriptor)| (name.as_str(), descriptor.clone())),
338 candidate: self.candidate.clone(),
339 descriptor: &self.descriptor,
340 label: &self.label,
341 }
342 .emit(&self.environment.types, &self.environment.lookup)
343 .map_err(|error| anyhow::anyhow!("{error}"))
344 }
345
346 fn key(&self) -> anyhow::Result<CellValue> {
352 let mut frame =
353 RawFrame::new(self.emit()?, &[], &self.descriptor, &self.environment.types)?;
354 frame.call(&[]).map(CellValue)
355 }
356}
357
358pub(crate) fn compile(
364 environment: Rc<Environment>,
365 modules: ModuleSet,
366) -> Result<CompiledModules, Vec<ModuleDiagnostic>> {
367 let checked = analysis::analyze(&modules, &environment.types, &environment.lookup)?;
368 debug_assert!(std::ptr::eq(checked.types, environment.types.as_ref()));
369 debug_assert!(std::ptr::eq(checked.lookup, &environment.lookup));
370 let mut completed: Vec<Option<(TypedTemplate, Vec<RootCell>)>> =
371 (0..checked.definitions.len()).map(|_| None).collect();
372 for &id in &checked.resolved.order {
374 let plan = &checked.definitions[id];
375 let result = if let Some(native) = plan.native {
376 Ok((native.clone(), Vec::new()))
377 } else {
378 materialize(plan, &checked.definitions, &environment, &completed)
379 };
380 completed[id] = Some(result.map_err(|mut error| {
381 error.labels.push(plan.label.clone());
382 vec![error]
383 })?);
384 }
385 let mut result = CompiledModules {
386 templates: HashMap::new(),
387 roots: HashMap::new(),
388 sources: modules.sources.sources().to_vec(),
389 environment: environment.clone(),
390 };
391 let mut seen_sources = result.sources.iter().cloned().collect();
392 let mut seen_definitions = HashSet::new();
393 for (id, &(module, local)) in checked.resolved.definitions.iter().enumerate() {
394 let entry = &modules.entries[module];
395 let name = &entry.definitions[local].name;
396 let (template, cells) = completed[id]
397 .take()
398 .expect("dependency order covers all definitions");
399 template.retain_sources(
400 &mut seen_definitions,
401 &mut seen_sources,
402 &mut result.sources,
403 );
404 if checked.definitions[id].component {
405 result
406 .templates
407 .entry(entry.path.clone())
408 .or_default()
409 .insert(name.clone(), template);
410 } else {
411 result
412 .roots
413 .entry(entry.path.clone())
414 .or_default()
415 .push((name.clone(), PreparedRoot { template, cells }));
416 }
417 }
418 Ok(result)
419}
420
421fn slots(ordinals: &[usize], cells: &[CellSlot]) -> Vec<CellSlot> {
426 debug_assert!(ordinals.iter().all(|id| *id < cells.len()));
427 ordinals.iter().map(|id| cells[*id].clone()).collect()
428}
429
430fn method(
436 builder: &CompositionBuilder,
437 plan: &DefinitionPlan<'_>,
438 environment: &Rc<Environment>,
439 cells: &[CellSlot],
440 outputs: &[usize],
441 destructure: bool,
442 expression: &analysis::Expression<'_>,
443) -> Result<MethodRecipe, ModuleDiagnostic> {
444 let inputs = slots(&expression.inputs, cells);
445 let outputs = slots(outputs, cells);
446 let owned = Expression::own(expression, plan, environment);
447 builder.method_pattern(&inputs, &outputs, destructure, move || owned.emit())
448}
449
450fn materialize(
457 plan: &DefinitionPlan<'_>,
458 definitions: &[DefinitionPlan<'_>],
459 environment: &Rc<Environment>,
460 completed: &[Option<(TypedTemplate, Vec<RootCell>)>],
461) -> Result<(TypedTemplate, Vec<RootCell>), ModuleDiagnostic> {
462 let mut builder = CompositionBuilder::with_types(environment.types.clone());
463 builder.set_label(plan.label.clone());
464 let mut cells = Vec::with_capacity(plan.cells.len());
465 let mut authored = Vec::with_capacity(plan.formals + plan.items.len());
466 for cell in &plan.cells[..plan.formals] {
467 let slot = builder.parameter(cell.name, cell.shape.clone())?;
468 authored.push(AuthoredItem {
469 role: AuthoredRole::Parameter,
470 parent: None,
471 label: cell.label.clone(),
472 cell: Some(slot.native().clone()),
473 relationship: None,
474 method_index: None,
475 requirement_name: None,
476 expression: None,
477 });
478 cells.push(slot);
479 }
480 let mut groups: Vec<Option<RelationshipSlot>> = vec![None; plan.items.len()];
481 let mut pending_conditional = None;
482 for (ordinal, item) in plan.items.iter().enumerate() {
484 let mut metadata = AuthoredItem {
485 role: AuthoredRole::Cell,
486 parent: item.parent.map(|p| p + plan.formals),
487 label: item.label.clone(),
488 cell: None,
489 relationship: None,
490 method_index: None,
491 requirement_name: None,
492 expression: None,
493 };
494 let lower = (|| -> Result<(), ModuleDiagnostic> {
495 match &item.body {
496 Body::Cell { cell, initializer } => {
497 debug_assert_eq!(*cell, cells.len());
498 let declaration = &plan.cells[*cell];
499 let source = declaration.kind == Kind::Source;
500 metadata.role = if source {
501 AuthoredRole::Source
502 } else {
503 AuthoredRole::Cell
504 };
505 metadata.expression = initializer
506 .as_ref()
507 .map(|expression| expression.label.clone());
508 let slot = if let Some(expression) = initializer {
509 let owned = Expression::own(expression, plan, environment);
510 if source {
511 builder.source(
512 declaration.name,
513 declaration.shape.clone(),
514 move || owned.emit(),
515 )?
516 } else {
517 builder.cell(
518 declaration.name,
519 declaration.shape.clone(),
520 move || owned.emit(),
521 )?
522 }
523 } else {
524 builder.default_private(
525 declaration.name,
526 declaration.shape.clone(),
527 source,
528 )?
529 };
530 metadata.cell = Some(slot.native().clone());
531 metadata.label = declaration.label.clone();
532 cells.push(slot);
533 }
534 Body::Out { cell, writer } => {
535 metadata.role = AuthoredRole::Out;
536 metadata.expression = Some(writer.label.clone());
537 debug_assert_eq!(*cell, cells.len());
538 let declaration = &plan.cells[*cell];
539 let slot = builder.default_private(
540 declaration.name,
541 declaration.shape.clone(),
542 false,
543 )?;
544 metadata.cell = Some(slot.native().clone());
545 metadata.label = declaration.label.clone();
546 cells.push(slot);
547 let recipe =
548 method(&builder, plan, environment, &cells, &[*cell], false, writer)?;
549 metadata.relationship = Some(builder.out(recipe)?);
550 metadata.method_index = Some(0);
551 }
552 Body::Filter {
553 cell,
554 expression,
555 range,
556 } => {
557 metadata.role = if *range {
558 AuthoredRole::RangeFilter
559 } else {
560 AuthoredRole::Filter
561 };
562 metadata.expression = Some(expression.label.clone());
563 let inputs = slots(&expression.inputs, &cells);
564 let owned = Expression::own(expression, plan, environment);
565 builder.filter(&cells[*cell], &inputs, move || owned.emit())?;
566 metadata.cell = Some(cells[*cell].native().clone());
567 }
568 Body::Requirement {
569 cell,
570 name,
571 expression,
572 } => {
573 metadata.role = AuthoredRole::Requirement;
574 metadata.expression = Some(expression.label.clone());
575 let inputs = slots(&expression.inputs, &cells);
576 let owned = Expression::own(expression, plan, environment);
577 builder.require(&cells[*cell], &inputs, move || owned.emit())?;
578 metadata.cell = Some(cells[*cell].native().clone());
579 metadata.requirement_name = name.map(str::to_owned);
580 }
581 Body::Relationship { methods } => {
582 metadata.role = AuthoredRole::Relationship;
583 let recipes = methods
584 .clone()
585 .map(|id| match &plan.items[id].body {
586 Body::Method {
587 outputs,
588 destructure,
589 expression,
590 } => method(
591 &builder,
592 plan,
593 environment,
594 &cells,
595 outputs,
596 *destructure,
597 expression,
598 ),
599 _ => unreachable!("checked relationship contains only methods"),
600 })
601 .collect::<Result<Vec<_>, _>>()?;
602 let group = builder.relationship(recipes)?;
603 metadata.relationship = Some(group.clone());
604 groups[ordinal] = Some(group);
605 }
606 Body::Method { expression, .. } => {
607 metadata.role = AuthoredRole::Method;
608 metadata.expression = Some(expression.label.clone());
609 let parent = item.parent.expect("method has relationship parent");
610 let Body::Relationship { methods } = &plan.items[parent].body else {
611 unreachable!()
612 };
613 metadata.relationship = groups[parent].clone();
614 metadata.method_index = Some(ordinal - methods.start);
615 }
616 Body::Use {
617 target,
618 bindings,
619 syntax,
620 } => {
621 metadata.role = AuthoredRole::Use;
622 if let Some(location) = &item.label.location {
623 metadata.label = label(&location.source, syntax.span, "component use");
624 }
625 let child = &completed[*target]
626 .as_ref()
627 .expect("dependency materialized")
628 .0;
629 let signature = &definitions[*target];
630 let actuals = signature.cells[..signature.formals]
631 .iter()
632 .zip(bindings)
633 .map(|(formal, id)| (formal.name, cells[*id].clone()))
634 .collect::<Vec<_>>();
635 let inclusion = builder.include(child, &actuals)?;
636 groups[ordinal] = Some(inclusion.relationships()[0].clone());
637 metadata.relationship = groups[ordinal].clone();
638 }
639 Body::Conditional { expression } => {
640 metadata.role = AuthoredRole::Conditional;
641 metadata.expression = Some(expression.label.clone());
642 pending_conditional = Some(ordinal);
643 }
644 Body::Branch { syntax } => {
645 metadata.role = AuthoredRole::Branch;
646 if let Some(syntax) = syntax
647 && let Some(location) = &item.label.location
648 {
649 metadata.expression =
650 Some(label(&location.source, syntax.literal_span, "branch key"));
651 }
652 }
653 }
654 Ok(())
655 })();
656 lower.map_err(|mut error| {
657 error.labels.push(item.label.clone());
658 error
659 })?;
660 authored.push(metadata);
661 let next_conditional = plan
664 .items
665 .get(ordinal + 1)
666 .is_none_or(|next| next.parent.is_none());
667 if next_conditional && let Some(id) = pending_conditional.take() {
668 lower_conditional(&mut builder, plan, id, environment, &cells, &groups)?;
669 }
670 }
671 let locals = if plan.component {
672 Vec::new()
673 } else {
674 plan.cells
675 .iter()
676 .zip(&cells)
677 .map(|(cell, slot)| RootCell {
678 name: cell.name.to_owned(),
679 shape: cell.shape.clone(),
680 slot: slot.native().clone(),
681 label: cell.label.clone(),
682 kind: match cell.kind {
683 Kind::Cell => CellKind::Cell,
684 Kind::Source => CellKind::Source,
685 Kind::Out => CellKind::Out,
686 Kind::Parameter => unreachable!("root has no formals"),
687 },
688 })
689 .collect()
690 };
691 builder.set_authored(authored);
692 Ok((builder.finish()?, locals))
693}
694
695fn lower_conditional(
701 builder: &mut CompositionBuilder,
702 plan: &DefinitionPlan<'_>,
703 id: usize,
704 environment: &Rc<Environment>,
705 cells: &[CellSlot],
706 groups: &[Option<RelationshipSlot>],
707) -> Result<(), ModuleDiagnostic> {
708 let Body::Conditional { expression } = &plan.items[id].body else {
709 unreachable!()
710 };
711 let inputs = slots(&expression.inputs, cells);
712 let owned = Expression::own(expression, plan, environment);
713 let shape = expression.shape.clone();
714 let mut keys = Vec::new();
715 let mut branches = Vec::new();
716 let mut default = Vec::new();
717 let mut current_branch = None;
718 for (item_id, item) in plan
719 .items
720 .iter()
721 .enumerate()
722 .skip(id + 1)
723 .take_while(|(_, item)| item.parent.is_some())
724 {
725 if let Body::Branch { syntax } = &item.body {
726 if let Some(syntax) = syntax {
727 let literal = cel_parser::literal_expr(&syntax.literal)
728 .map_err(crate::composition::diagnostic)?;
729 let syntax = if syntax.negated {
730 Expr::Op {
731 name: "-".into(),
732 operands: vec![literal],
733 span: syntax.literal_span,
734 }
735 } else {
736 literal
737 };
738 keys.push(Expression {
739 syntax,
740 inputs: Vec::new(),
741 candidate: None,
742 descriptor: owned.descriptor.clone(),
743 label: item.label.clone(),
744 environment: environment.clone(),
745 });
746 branches.push(Vec::new());
747 current_branch = Some(branches.len() - 1);
748 } else {
749 current_branch = None;
750 }
751 } else if let Some(group) = &groups[item_id] {
752 if let Some(branch) = current_branch {
753 branches[branch].push(group.clone());
754 } else {
755 default.push(group.clone());
756 }
757 }
758 }
759 builder.conditional_owned(
760 &inputs,
761 shape,
762 move || owned.emit(),
763 move || {
764 keys.iter()
765 .map(|key| key.key().map(|value| vec![value]))
766 .collect()
767 },
768 branches,
769 default,
770 )
771}
772
773#[cfg(test)]
774mod tests {
775 use super::*;
776
777 #[test]
779 fn prepared_roots_use_component_lowering_without_private_name_flattening() {
780 let path: ModulePath = "roots".parse().unwrap();
781 let mut modules = ModuleSet::new();
782 modules
783 .insert_source(
784 path.clone(),
785 "roots.adm2",
786 "sheet hidden(a: i32, b: i32) { cell private: i32 = 4;
787 relationship { b := a + private; } }
788 sheet root { source input: i32 = 3; cell output: i32;
789 relationship hidden(a: input, b: output);
790 out answer := output; }",
791 )
792 .unwrap();
793 let compiled = ModuleCompiler::new(TypeRegistry::new(), OpLookup::new())
794 .compile(modules)
795 .unwrap();
796 let root = compiled.prepared_root(&path, "root").unwrap();
797 let mut destination = crate::ParsedSheet::new();
798 let instance = root.template.instantiate(&mut destination, &[]).unwrap();
799 let root_id = instance.occurrences()[0].id();
800 let output = instance.cell(root_id, &root.cells()[1].slot).unwrap();
801 let answer = instance.cell(root_id, &root.cells()[2].slot).unwrap();
802 assert_eq!(
803 root.cells()
804 .iter()
805 .map(|cell| cell.name.as_str())
806 .collect::<Vec<_>>(),
807 ["input", "output", "answer"]
808 );
809 assert_eq!(root.cells()[2].kind, CellKind::Out);
810 assert_eq!(destination.cell_kind(answer), Some(CellKind::Out));
811 drop((instance, compiled));
812 destination.propagate().unwrap();
813 assert_eq!(*destination.read::<i32>(output).unwrap(), 7);
814 assert_eq!(*destination.read::<i32>(answer).unwrap(), 7);
815 assert!(destination.cell_names.is_empty());
816 }
817}