1use std::{collections::BTreeMap, sync::Arc};
4
5use sim_kernel::{
6 Args, CORE_FUNCTION_CLASS_ID, Callable, ClassRef, Cx, Error, Object, ObjectCompat, Result,
7 Symbol, Value,
8};
9
10use crate::{LanguageProfile, ProfileRegistry};
11
12pub type ProfileFunctionBody = Arc<dyn Fn(&mut Cx, Args) -> Result<Value> + Send + Sync>;
14
15#[derive(Clone)]
17pub struct ProfileFunction {
18 defining_profile: Symbol,
19 organ: Symbol,
20 function: Symbol,
21 body: ProfileFunctionBody,
22}
23
24impl ProfileFunction {
25 pub fn new<F>(defining_profile: Symbol, organ: Symbol, function: Symbol, body: F) -> Self
28 where
29 F: Fn(&mut Cx, Args) -> Result<Value> + Send + Sync + 'static,
30 {
31 Self {
32 defining_profile,
33 organ,
34 function,
35 body: Arc::new(body),
36 }
37 }
38
39 pub fn defining_profile(&self) -> &Symbol {
41 &self.defining_profile
42 }
43
44 pub fn organ(&self) -> &Symbol {
46 &self.organ
47 }
48
49 pub fn function(&self) -> &Symbol {
51 &self.function
52 }
53}
54
55impl Callable for ProfileFunction {
56 fn call(&self, cx: &mut Cx, args: Args) -> Result<Value> {
57 (self.body)(cx, args)
58 }
59}
60
61impl Object for ProfileFunction {
62 fn display(&self, _cx: &mut Cx) -> Result<String> {
63 Ok(format!(
64 "#<profile-function {} defined-by {}>",
65 self.function, self.defining_profile
66 ))
67 }
68
69 fn as_any(&self) -> &dyn std::any::Any {
70 self
71 }
72}
73
74impl ObjectCompat for ProfileFunction {
75 fn class(&self, cx: &mut Cx) -> Result<ClassRef> {
76 cx.factory().class_stub(
77 CORE_FUNCTION_CLASS_ID,
78 Symbol::qualified("core", "Function"),
79 )
80 }
81
82 fn as_callable(&self) -> Option<&dyn Callable> {
83 Some(self)
84 }
85}
86
87#[derive(Clone, Debug)]
90pub struct ProfileFunctionBinding {
91 pub defining_profile: Symbol,
93 pub organ: Symbol,
95 pub function: Symbol,
97 pub value: Value,
99}
100
101#[derive(Clone, Debug, Default)]
104pub struct SharedOrganRuntime {
105 registry: ProfileRegistry,
106 functions: BTreeMap<Symbol, ProfileFunctionBinding>,
107}
108
109impl SharedOrganRuntime {
110 pub fn new() -> Self {
112 Self::default()
113 }
114
115 pub fn register_profile(&mut self, profile: LanguageProfile) -> Result<()> {
117 self.registry.register_profile(profile)
118 }
119
120 pub fn profile(&self, symbol: &Symbol) -> Option<&LanguageProfile> {
122 self.registry.profile(symbol)
123 }
124
125 pub fn profiles(&self) -> impl Iterator<Item = &LanguageProfile> {
127 self.registry.profiles()
128 }
129
130 pub fn define_function(
135 &mut self,
136 defining_profile: &Symbol,
137 organ: Symbol,
138 function: Symbol,
139 value: Value,
140 ) -> Result<()> {
141 self.require_profile_uses_organ(defining_profile, &organ)?;
142 if value.object().as_callable().is_none() {
143 return Err(Error::TypeMismatch {
144 expected: "callable",
145 found: "non-callable",
146 });
147 }
148 if self.functions.contains_key(&function) {
149 return Err(Error::DuplicateExport {
150 kind: "standard-profile-function",
151 symbol: function,
152 });
153 }
154 self.functions.insert(
155 function.clone(),
156 ProfileFunctionBinding {
157 defining_profile: defining_profile.clone(),
158 organ,
159 function,
160 value,
161 },
162 );
163 Ok(())
164 }
165
166 pub fn function(&self, function: &Symbol) -> Option<&ProfileFunctionBinding> {
168 self.functions.get(function)
169 }
170
171 pub fn call_function(
174 &self,
175 cx: &mut Cx,
176 calling_profile: &Symbol,
177 function: &Symbol,
178 args: Vec<Value>,
179 ) -> Result<Value> {
180 let binding = self
181 .functions
182 .get(function)
183 .ok_or_else(|| Error::UnknownFunction {
184 function: function.clone(),
185 })?;
186 self.require_profile_uses_organ(calling_profile, &binding.organ)?;
187 let callable = binding
188 .value
189 .object()
190 .as_callable()
191 .ok_or(Error::TypeMismatch {
192 expected: "callable",
193 found: "non-callable",
194 })?;
195 callable.call(cx, Args::new(args))
196 }
197
198 fn require_profile_uses_organ(&self, profile: &Symbol, organ: &Symbol) -> Result<()> {
199 let profile_record =
200 self.registry
201 .profile(profile)
202 .ok_or_else(|| Error::UnknownSymbol {
203 symbol: profile.clone(),
204 })?;
205 if profile_record
206 .organs
207 .iter()
208 .any(|used| &used.organ == organ)
209 {
210 Ok(())
211 } else {
212 Err(Error::Eval(format!(
213 "profile {profile} does not use organ {organ}"
214 )))
215 }
216 }
217}
218
219pub fn profile_function_value<F>(
221 cx: &mut Cx,
222 defining_profile: Symbol,
223 organ: Symbol,
224 function: Symbol,
225 body: F,
226) -> Result<Value>
227where
228 F: Fn(&mut Cx, Args) -> Result<Value> + Send + Sync + 'static,
229{
230 cx.factory().opaque(Arc::new(ProfileFunction::new(
231 defining_profile,
232 organ,
233 function,
234 body,
235 )))
236}