1use crate::ast::ArgSep;
20use crate::ast::ExprType;
21use crate::bytecode::TaggedRegisterRef;
22use crate::bytecode::VarArgTag;
23use crate::mem::HeapOverflowError;
24use crate::mem::{ConstantDatum, DatumPtr, Heap, HeapDatum};
25use crate::reader::LineCol;
26use async_trait::async_trait;
27use std::borrow::Cow;
28use std::fmt;
29use std::io;
30use std::ops::RangeInclusive;
31use std::rc::Rc;
32use std::str::Lines;
33
34#[derive(Debug, thiserror::Error)]
36pub enum CallError {
37 #[error("{0}")]
39 Argument(String),
40
41 #[error("{0}")]
43 Eval(String),
44
45 #[error("{0}")]
47 IoError(#[from] io::Error),
48
49 #[error("{0}")]
51 Precondition(String),
52
53 #[error("{0}: {1}")]
55 Syntax(LineCol, String),
56}
57
58impl From<HeapOverflowError> for CallError {
59 fn from(value: HeapOverflowError) -> Self {
60 Self::Eval(value.to_string())
61 }
62}
63
64impl CallError {
65 pub(crate) fn to_upcall_error(&self, default_pos: LineCol) -> UpcallError {
70 match self {
71 CallError::Argument(message) => UpcallError::Argument(default_pos, message.clone()),
72
73 CallError::Eval(message) => UpcallError::Eval(default_pos, message.clone()),
74
75 CallError::IoError(e) => UpcallError::IoError(default_pos, e.to_string()),
76
77 CallError::Precondition(message) => {
78 UpcallError::Precondition(default_pos, message.clone())
79 }
80
81 CallError::Syntax(pos, message) => UpcallError::Syntax(*pos, message.clone()),
82 }
83 }
84}
85
86pub type CallResult<T> = Result<T, CallError>;
88
89#[derive(Debug, thiserror::Error)]
93pub enum UpcallError {
94 #[error("{0}: {1}")]
96 Argument(LineCol, String),
97
98 #[error("{0}: {1}")]
100 Eval(LineCol, String),
101
102 #[error("{0}: {1}")]
104 IoError(LineCol, String),
105
106 #[error("{0}: {1}")]
108 Precondition(LineCol, String),
109
110 #[error("{0}: {1}")]
112 Syntax(LineCol, String),
113}
114
115impl UpcallError {
116 pub fn parts(&self) -> (LineCol, String) {
118 match self {
119 UpcallError::Argument(pos, message) => (*pos, message.clone()),
120 UpcallError::Eval(pos, message) => (*pos, message.clone()),
121 UpcallError::IoError(pos, message) => (*pos, message.clone()),
122 UpcallError::Precondition(pos, message) => (*pos, message.clone()),
123 UpcallError::Syntax(pos, message) => (*pos, message.clone()),
124 }
125 }
126}
127
128#[derive(Clone, Debug, PartialEq)]
130pub struct RequiredValueSyntax {
131 pub name: Cow<'static, str>,
133
134 pub vtype: ExprType,
136}
137
138#[derive(Clone, Debug, PartialEq)]
140pub struct RequiredRefSyntax {
141 pub name: Cow<'static, str>,
143
144 pub require_array: bool,
146
147 pub define_undefined: bool,
151}
152
153#[derive(Clone, Debug, PartialEq)]
157pub struct OptionalValueSyntax {
158 pub name: Cow<'static, str>,
160
161 pub vtype: ExprType,
163}
164
165#[derive(Clone, Debug, PartialEq)]
167pub enum RepeatedTypeSyntax {
168 AnyValue,
171
172 TypedValue(ExprType),
174
175 VariableRef,
177}
178
179#[derive(Clone, Debug, PartialEq)]
183pub struct RepeatedSyntax {
184 pub name: Cow<'static, str>,
186
187 pub type_syn: RepeatedTypeSyntax,
189
190 pub sep: ArgSepSyntax,
193
194 pub require_one: bool,
196
197 pub allow_missing: bool,
199}
200
201impl RepeatedSyntax {
202 fn describe(&self, output: &mut String, last_singular_sep: Option<&ArgSepSyntax>) {
207 if !self.require_one {
208 output.push('[');
209 }
210
211 if let Some(sep) = last_singular_sep {
212 sep.describe(output);
213 }
214
215 output.push_str(&self.name);
216 output.push('1');
217 if let RepeatedTypeSyntax::TypedValue(vtype) = self.type_syn {
218 output.push(vtype.annotation());
219 }
220
221 if self.require_one {
222 output.push('[');
223 }
224
225 self.sep.describe(output);
226 output.push_str("..");
227 self.sep.describe(output);
228
229 output.push_str(&self.name);
230 output.push('N');
231 if let RepeatedTypeSyntax::TypedValue(vtype) = self.type_syn {
232 output.push(vtype.annotation());
233 }
234
235 output.push(']');
236 }
237}
238
239#[derive(Clone, Debug, PartialEq)]
241pub struct AnyValueSyntax {
242 pub name: Cow<'static, str>,
244
245 pub allow_missing: bool,
247}
248
249#[derive(Copy, Clone, Debug, PartialEq)]
251pub enum ArgSepSyntax {
252 Exactly(ArgSep),
254
255 OneOf(&'static [ArgSep]),
257
258 End,
260}
261
262impl ArgSepSyntax {
263 fn describe(&self, output: &mut String) {
265 match self {
266 ArgSepSyntax::Exactly(sep) => {
267 let (text, needs_space) = sep.describe();
268
269 if !text.is_empty() && needs_space {
270 output.push(' ');
271 }
272 output.push_str(text);
273 if !text.is_empty() {
274 output.push(' ');
275 }
276 }
277
278 ArgSepSyntax::OneOf(seps) => {
279 output.push_str(" <");
280 for (i, sep) in seps.iter().enumerate() {
281 let (text, _needs_space) = sep.describe();
282 output.push_str(text);
283 if i < seps.len() - 1 {
284 output.push('|');
285 }
286 }
287 output.push_str("> ");
288 }
289
290 ArgSepSyntax::End => (),
291 };
292 }
293}
294
295#[derive(Clone, Debug, PartialEq)]
300pub enum SingularArgSyntax {
301 RequiredValue(RequiredValueSyntax, ArgSepSyntax),
303
304 RequiredRef(RequiredRefSyntax, ArgSepSyntax),
306
307 OptionalValue(OptionalValueSyntax, ArgSepSyntax),
309
310 AnyValue(AnyValueSyntax, ArgSepSyntax),
312}
313
314#[derive(Clone, Debug, PartialEq)]
322pub(crate) struct CallableSyntax {
323 pub(crate) singular: Cow<'static, [SingularArgSyntax]>,
325
326 pub(crate) repeated: Option<Cow<'static, RepeatedSyntax>>,
328}
329
330impl CallableSyntax {
331 pub(crate) fn new_static(
334 singular: &'static [SingularArgSyntax],
335 repeated: Option<&'static RepeatedSyntax>,
336 ) -> Self {
337 Self { singular: Cow::Borrowed(singular), repeated: repeated.map(Cow::Borrowed) }
338 }
339
340 pub(crate) fn new_dynamic(
343 singular: Vec<SingularArgSyntax>,
344 repeated: Option<RepeatedSyntax>,
345 ) -> Self {
346 Self { singular: Cow::Owned(singular), repeated: repeated.map(Cow::Owned) }
347 }
348
349 pub(crate) fn expected_nargs(&self) -> RangeInclusive<usize> {
351 let mut min = self.singular.len();
352 let mut max = self.singular.len();
353
354 if let Some(syn) = self.repeated.as_ref() {
355 if syn.require_one {
356 min += 1;
357 }
358 max = usize::MAX;
359 }
360
361 min..=max
362 }
363
364 pub(crate) fn is_empty(&self) -> bool {
366 self.singular.is_empty() && self.repeated.is_none()
367 }
368
369 pub(crate) fn describe(&self) -> String {
371 let mut description = String::new();
372 let mut last_singular_sep = None;
373 for (i, s) in self.singular.iter().enumerate() {
374 let sep = match s {
375 SingularArgSyntax::RequiredValue(details, sep) => {
376 description.push_str(&details.name);
377 description.push(details.vtype.annotation());
378 sep
379 }
380
381 SingularArgSyntax::RequiredRef(details, sep) => {
382 description.push_str(&details.name);
383 sep
384 }
385
386 SingularArgSyntax::OptionalValue(details, sep) => {
387 description.push('[');
388 description.push_str(&details.name);
389 description.push(details.vtype.annotation());
390 description.push(']');
391 sep
392 }
393
394 SingularArgSyntax::AnyValue(details, sep) => {
395 if details.allow_missing {
396 description.push('[');
397 }
398 description.push_str(&details.name);
399 if details.allow_missing {
400 description.push(']');
401 }
402 sep
403 }
404 };
405
406 if self.repeated.is_none() || i < self.singular.len() - 1 {
407 sep.describe(&mut description);
408 }
409 if i == self.singular.len() - 1 {
410 last_singular_sep = Some(sep);
411 }
412 }
413
414 if let Some(syn) = &self.repeated {
415 syn.describe(&mut description, last_singular_sep);
416 }
417
418 description
419 }
420}
421
422pub struct CallableMetadataBuilder {
424 name: Cow<'static, str>,
426
427 return_type: Option<ExprType>,
429
430 is_async: bool,
432
433 category: Option<&'static str>,
435
436 syntaxes: Vec<CallableSyntax>,
438
439 description: Option<&'static str>,
441}
442
443impl CallableMetadataBuilder {
444 pub fn new(name: &'static str) -> Self {
450 assert!(name == name.to_ascii_uppercase(), "Callable name must be in uppercase");
451
452 Self {
453 name: Cow::Borrowed(name),
454 return_type: None,
455 is_async: false,
456 syntaxes: vec![],
457 category: None,
458 description: None,
459 }
460 }
461
462 pub fn new_dynamic<S: Into<String>>(name: S) -> Self {
467 Self {
468 name: Cow::Owned(name.into().to_ascii_uppercase()),
469 return_type: None,
470 is_async: false,
471 syntaxes: vec![],
472 category: Some("User defined"),
473 description: Some("User defined symbol."),
474 }
475 }
476
477 pub fn with_return_type(mut self, return_type: ExprType) -> Self {
479 self.return_type = Some(return_type);
480 self
481 }
482
483 pub fn with_async(mut self, is_async: bool) -> Self {
485 self.is_async = is_async;
486 self
487 }
488
489 pub fn with_syntax(
491 mut self,
492 syntaxes: &'static [(&'static [SingularArgSyntax], Option<&'static RepeatedSyntax>)],
493 ) -> Self {
494 self.syntaxes = syntaxes
495 .iter()
496 .map(|s| CallableSyntax::new_static(s.0, s.1))
497 .collect::<Vec<CallableSyntax>>();
498 self
499 }
500
501 pub(crate) fn with_syntaxes<S: Into<Vec<CallableSyntax>>>(mut self, syntaxes: S) -> Self {
503 self.syntaxes = syntaxes.into();
504 self
505 }
506
507 pub(crate) fn with_dynamic_syntax(
509 self,
510 syntaxes: Vec<(Vec<SingularArgSyntax>, Option<RepeatedSyntax>)>,
511 ) -> Self {
512 let syntaxes = syntaxes
513 .into_iter()
514 .map(|s| CallableSyntax::new_dynamic(s.0, s.1))
515 .collect::<Vec<CallableSyntax>>();
516 self.with_syntaxes(syntaxes)
517 }
518
519 pub fn with_category(mut self, category: &'static str) -> Self {
522 self.category = Some(category);
523 self
524 }
525
526 pub fn with_description(mut self, description: &'static str) -> Self {
531 for l in description.lines() {
532 assert!(!l.is_empty(), "Description cannot contain empty lines");
533 }
534 self.description = Some(description);
535 self
536 }
537
538 pub fn build(self) -> Rc<CallableMetadata> {
540 assert!(!self.syntaxes.is_empty(), "All callables must specify a syntax");
541 Rc::from(CallableMetadata {
542 name: self.name,
543 return_type: self.return_type,
544 is_async: self.is_async,
545 syntaxes: self.syntaxes,
546 category: self.category.expect("All callables must specify a category"),
547 description: self.description.expect("All callables must specify a description"),
548 })
549 }
550
551 pub fn test_build(mut self) -> Rc<CallableMetadata> {
554 if self.syntaxes.is_empty() {
555 self.syntaxes.push(CallableSyntax::new_static(&[], None));
556 }
557 Rc::from(CallableMetadata {
558 name: self.name,
559 return_type: self.return_type,
560 is_async: self.is_async,
561 syntaxes: self.syntaxes,
562 category: self.category.unwrap_or(""),
563 description: self.description.unwrap_or(""),
564 })
565 }
566}
567
568#[derive(Clone, Debug, PartialEq)]
573pub struct CallableMetadata {
574 name: Cow<'static, str>,
576
577 return_type: Option<ExprType>,
579
580 is_async: bool,
582
583 syntaxes: Vec<CallableSyntax>,
585
586 category: &'static str,
588
589 description: &'static str,
591}
592
593impl CallableMetadata {
594 pub fn name(&self) -> &str {
596 &self.name
597 }
598
599 pub fn return_type(&self) -> Option<ExprType> {
601 self.return_type
602 }
603
604 pub fn is_async(&self) -> bool {
606 self.is_async
607 }
608
609 pub fn syntax(&self) -> String {
611 fn format_one(cs: &CallableSyntax) -> String {
612 let mut syntax = cs.describe();
613 if syntax.is_empty() {
614 syntax.push_str("no arguments");
615 }
616 syntax
617 }
618
619 match self.syntaxes.as_slice() {
620 [] => panic!("Callables without syntaxes are not allowed at construction time"),
621 [one] => format_one(one),
622 many => many
623 .iter()
624 .map(|syn| format!("<{}>", syn.describe()))
625 .collect::<Vec<String>>()
626 .join(" | "),
627 }
628 }
629
630 fn is_function_sep(sep: &ArgSepSyntax) -> bool {
633 match sep {
634 ArgSepSyntax::Exactly(ArgSep::Long) | ArgSepSyntax::End => true,
635 ArgSepSyntax::OneOf(seps) => seps.iter().all(|s| *s == ArgSep::Long),
636 _ => false,
637 }
638 }
639
640 fn debug_assert_function_seps(&self, syntax: &CallableSyntax) {
644 if self.return_type().is_none() {
645 return;
646 }
647 for syn in syntax.singular.iter() {
648 let sep = match syn {
649 SingularArgSyntax::RequiredValue(_, sep) => sep,
650 SingularArgSyntax::RequiredRef(_, sep) => sep,
651 SingularArgSyntax::OptionalValue(_, sep) => sep,
652 SingularArgSyntax::AnyValue(_, sep) => sep,
653 };
654 debug_assert!(
655 Self::is_function_sep(sep),
656 "Function {} has a non-comma separator in its singular args syntax",
657 self.name()
658 );
659 }
660 if let Some(repeated) = syntax.repeated.as_ref() {
661 debug_assert!(
662 Self::is_function_sep(&repeated.sep),
663 "Function {} has a non-comma separator in its repeated args syntax",
664 self.name()
665 );
666 }
667 }
668
669 pub(crate) fn find_syntax(&self, nargs: usize) -> Option<&CallableSyntax> {
674 let mut matches = self.syntaxes.iter().filter(|s| s.expected_nargs().contains(&nargs));
675 let syntax = matches.next();
676 match syntax {
677 Some(syntax) => {
678 debug_assert!(matches.next().is_none(), "Ambiguous syntax definitions");
679 if cfg!(debug_assertions) {
680 self.debug_assert_function_seps(syntax);
681 }
682 Some(syntax)
683 }
684 None => None,
685 }
686 }
687
688 #[allow(unused)]
691 pub fn category(&self) -> &'static str {
692 self.category
693 }
694
695 #[allow(unused)]
698 pub fn description(&self) -> Lines<'static> {
699 self.description.lines()
700 }
701
702 #[allow(unused)]
704 pub fn is_argless(&self) -> bool {
705 self.syntaxes.is_empty() || (self.syntaxes.len() == 1 && self.syntaxes[0].is_empty())
706 }
707
708 #[allow(unused)]
710 pub(crate) fn is_function(&self) -> bool {
711 self.return_type.is_some()
712 }
713
714 pub(crate) fn is_user_defined(&self) -> bool {
716 self.category == "User defined"
717 }
718}
719
720fn deref_boolean(regs: &[u64], index: usize, vtype: ExprType) -> bool {
722 assert_eq!(ExprType::Boolean, vtype);
723 regs[index] != 0
724}
725
726fn deref_double(regs: &[u64], index: usize, vtype: ExprType) -> f64 {
728 assert_eq!(ExprType::Double, vtype);
729 f64::from_bits(regs[index])
730}
731
732fn deref_integer(regs: &[u64], index: usize, vtype: ExprType) -> i32 {
734 assert_eq!(ExprType::Integer, vtype);
735 regs[index] as i32
736}
737
738fn deref_string<'a>(
740 regs: &[u64],
741 index: usize,
742 vtype: ExprType,
743 constants: &'a [ConstantDatum],
744 heap: &'a Heap,
745) -> &'a str {
746 assert_eq!(ExprType::Text, vtype);
747 let ptr = DatumPtr::from(regs[index]);
748 ptr.resolve_string(constants, heap)
749}
750
751fn array_dimensions<'a>(regs: &'a [u64], index: usize, heap: &'a Heap) -> &'a [usize] {
753 let ptr = DatumPtr::from(regs[index]);
754 let heap_idx = ptr.heap_index();
755 let HeapDatum::Array(a) = heap.get(heap_idx) else {
756 panic!("Scalar variable does not point to an array on the heap");
757 };
758 &a.dimensions
759}
760
761pub struct RegisterRef<'a, 'vm> {
764 scope: &'a Scope<'vm>,
766
767 index: usize,
769
770 pub vtype: ExprType,
772}
773
774impl<'a, 'vm> RegisterRef<'a, 'vm> {
775 pub fn deref_boolean(&self) -> bool {
777 deref_boolean(self.scope.regs, self.index, self.vtype)
778 }
779
780 pub fn deref_double(&self) -> f64 {
782 deref_double(self.scope.regs, self.index, self.vtype)
783 }
784
785 pub fn deref_integer(&self) -> i32 {
787 deref_integer(self.scope.regs, self.index, self.vtype)
788 }
789
790 pub fn deref_string(&self) -> &str {
792 deref_string(self.scope.regs, self.index, self.vtype, self.scope.constants, self.scope.heap)
793 }
794
795 pub fn array_dimensions(&self) -> &[usize] {
797 array_dimensions(self.scope.regs, self.index, self.scope.heap)
798 }
799}
800
801impl<'a, 'vm> fmt::Display for RegisterRef<'a, 'vm> {
802 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
803 write!(f, "&[R{}]{}", self.index, self.vtype)
804 }
805}
806
807pub struct RegisterRefMut<'a, 'vm> {
810 scope: &'a mut Scope<'vm>,
812
813 index: usize,
815
816 pub vtype: ExprType,
818}
819
820impl<'a, 'vm> RegisterRefMut<'a, 'vm> {
821 pub fn deref_boolean(&self) -> bool {
823 deref_boolean(self.scope.regs, self.index, self.vtype)
824 }
825
826 pub fn deref_double(&self) -> f64 {
828 deref_double(self.scope.regs, self.index, self.vtype)
829 }
830
831 pub fn deref_integer(&self) -> i32 {
833 deref_integer(self.scope.regs, self.index, self.vtype)
834 }
835
836 pub fn deref_string(&self) -> &str {
838 deref_string(self.scope.regs, self.index, self.vtype, self.scope.constants, self.scope.heap)
839 }
840
841 pub fn array_dimensions(&self) -> &[usize] {
843 array_dimensions(self.scope.regs, self.index, self.scope.heap)
844 }
845
846 pub fn set_boolean(&mut self, b: bool) {
848 assert_eq!(ExprType::Boolean, self.vtype);
849 self.scope.regs[self.index] = if b { 1 } else { 0 };
850 }
851
852 pub fn set_double(&mut self, d: f64) {
854 assert_eq!(ExprType::Double, self.vtype);
855 self.scope.regs[self.index] = d.to_bits();
856 }
857
858 pub fn set_integer(&mut self, i: i32) {
860 assert_eq!(ExprType::Integer, self.vtype);
861 self.scope.regs[self.index] = i as u64;
862 }
863
864 pub fn set_string<S: Into<String>>(&mut self, s: S) -> CallResult<()> {
866 assert_eq!(ExprType::Text, self.vtype);
867 self.scope.regs[self.index] = self.scope.heap.push(HeapDatum::Text(s.into()))?;
868 Ok(())
869 }
870}
871
872impl<'a, 'vm> fmt::Display for RegisterRefMut<'a, 'vm> {
873 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
874 write!(f, "&[R{}]{}", self.index, self.vtype)
875 }
876}
877
878pub struct Scope<'a> {
880 pub(crate) regs: &'a mut [u64],
882
883 pub(crate) constants: &'a [ConstantDatum],
885
886 pub(crate) heap: &'a mut Heap,
888
889 pub(crate) fp: usize,
891
892 pub(crate) arg_offset: usize,
899
900 pub(crate) arg_linecols: &'a [LineCol],
906
907 pub(crate) last_error: &'a Option<(LineCol, String)>,
909
910 pub(crate) data: &'a [Option<ConstantDatum>],
912}
913
914impl<'a> Scope<'a> {
915 pub fn data(&self) -> &[Option<ConstantDatum>] {
917 self.data
918 }
919
920 pub fn nargs(&self) -> usize {
922 self.arg_linecols.len()
923 }
924
925 pub fn get_pos(&self, arg: u8) -> LineCol {
929 self.arg_linecols[usize::from(arg)]
930 }
931
932 pub fn get_type(&self, arg: u8) -> VarArgTag {
934 VarArgTag::parse_u64(self.regs[self.fp + self.arg_offset + (arg as usize)]).unwrap()
935 }
936
937 pub fn get_boolean(&self, arg: u8) -> bool {
939 self.regs[self.fp + self.arg_offset + (arg as usize)] != 0
940 }
941
942 pub fn get_double(&self, arg: u8) -> f64 {
944 f64::from_bits(self.regs[self.fp + self.arg_offset + (arg as usize)])
945 }
946
947 pub fn get_integer(&self, arg: u8) -> i32 {
949 self.regs[self.fp + self.arg_offset + (arg as usize)] as i32
950 }
951
952 pub fn get_ref(&self, arg: u8) -> RegisterRef<'_, 'a> {
954 let tagged_ptr = self.regs[self.fp + self.arg_offset + (arg as usize)];
955 let (index, vtype) = TaggedRegisterRef::from_u64(tagged_ptr).parse();
956 RegisterRef { scope: self, index, vtype }
957 }
958
959 pub fn get_mut_ref(&mut self, arg: u8) -> RegisterRefMut<'_, 'a> {
961 let tagged_ptr = self.regs[self.fp + self.arg_offset + (arg as usize)];
962 let (index, vtype) = TaggedRegisterRef::from_u64(tagged_ptr).parse();
963 RegisterRefMut { scope: self, index, vtype }
964 }
965
966 pub fn get_string(&self, arg: u8) -> &str {
968 let index = self.regs[self.fp + self.arg_offset + (arg as usize)];
969 let ptr = DatumPtr::from(index);
970 ptr.resolve_string(self.constants, self.heap)
971 }
972
973 pub fn last_error(&self) -> Option<(LineCol, &str)> {
975 self.last_error.as_ref().map(|(pos, message)| (*pos, message.as_str()))
976 }
977
978 pub fn return_boolean(self, b: bool) -> CallResult<()> {
983 self.regs[self.fp] = if b { 1 } else { 0 };
984 Ok(())
985 }
986
987 pub fn return_double(self, d: f64) -> CallResult<()> {
992 self.regs[self.fp] = d.to_bits();
993 Ok(())
994 }
995
996 pub fn return_integer(self, i: i32) -> CallResult<()> {
1001 self.regs[self.fp] = i as u64;
1002 Ok(())
1003 }
1004
1005 pub fn return_string<S: Into<String>>(self, s: S) -> CallResult<()> {
1010 self.regs[self.fp] = self.heap.push(HeapDatum::Text(s.into()))?;
1011 Ok(())
1012 }
1013}
1014
1015#[async_trait(?Send)]
1024pub trait Callable {
1025 fn metadata(&self) -> Rc<CallableMetadata>;
1030
1031 fn exec(&self, _scope: Scope<'_>) -> CallResult<()> {
1033 unimplemented!("Must be implemented for !is_async callables")
1034 }
1035
1036 async fn async_exec(&self, _scope: Scope<'_>) -> CallResult<()> {
1038 unimplemented!("Must be implemented for is_async callables")
1039 }
1040}