sphinx/runtime/function/
signature.rs

1use core::fmt;
2use crate::language::Access;
3use crate::runtime::Variant;
4use crate::runtime::strings::{StringValue, StringSymbol, StrBuffer, STRING_TABLE};
5use crate::runtime::errors::{ExecResult, RuntimeError};
6
7
8#[derive(Clone, Debug)]
9pub struct Signature {
10    display: String,
11    name: Option<StringSymbol>,
12    required: Box<[Parameter]>,
13    default: Box<[Parameter]>,
14    variadic: Option<Parameter>,
15}
16
17impl Signature {
18    pub fn new(name: Option<impl Into<StringSymbol>>, required: Vec<Parameter>, default: Vec<Parameter>, variadic: Option<Parameter>) -> Self {
19        let name = name.map(|name| name.into());
20        
21        // build this once and cache the result, because it's expensive
22        let display = format_signature(name.as_ref(), &required, &default, variadic.as_ref());
23        
24        Self {
25            name,
26            display,
27            required: required.into_boxed_slice(),
28            default: default.into_boxed_slice(),
29            variadic,
30        }
31    }
32    
33    pub fn name(&self) -> Option<StringSymbol> { self.name }
34    
35    pub fn fmt_signature(&self) -> StringValue {
36        StringValue::new_uninterned(&self.display)
37    }
38    
39    pub fn fmt_name(&self) -> StringValue {
40        fn write_name(name: Option<StringSymbol>, fmt: &mut impl fmt::Write) -> fmt::Result {
41            if let Some(name) = name {
42                write!(fmt, "function \"{}()\"", name)
43            } else {
44                fmt.write_str("anonymous function")
45            }
46        }
47        
48        let mut buf = StrBuffer::<64>::new();
49        if write_name(self.name, &mut buf).is_ok() {
50            StringValue::new_maybe_interned(buf)
51        } else {
52            let mut buf = String::new();
53            write_name(self.name, &mut buf).ok();
54            StringValue::new_maybe_interned(buf)
55        }
56    }
57    
58    
59    pub fn required(&self) -> &[Parameter] { &self.required }
60    pub fn default(&self) -> &[Parameter] { &self.default }
61    pub fn variadic(&self) -> Option<&Parameter> { self.variadic.as_ref() }
62    
63    pub fn min_arity(&self) -> usize {
64        self.required.len()
65    }
66    
67    pub fn max_arity(&self) -> Option<usize> {
68        if self.variadic().is_some() { None }
69        else { Some(self.required.len() + self.default.len()) }
70    }
71    
72    pub fn check_args(&self, args: &[Variant]) -> ExecResult<()> {
73        if args.len() < self.required.len() {
74            return Err(RuntimeError::missing_arguments(self, args.len()))
75        }
76        
77        if matches!(self.max_arity(), Some(max_arity) if args.len() > max_arity) {
78            return Err(RuntimeError::too_many_arguments(self, args.len()))
79        }
80        
81        Ok(())
82    }
83    
84    pub fn param_count(&self) -> usize {
85        self.required.len()
86        + self.default.len()
87        + usize::from(self.variadic.is_some())
88    }
89    
90    /// Get the length of the argument buffer required by bind_args()
91    pub fn arg_len(&self) -> usize {
92        self.required.len() + self.default.len()
93    }
94    
95    /// Helper for native functions. Prepares a complete argument buffer by cloning argument values,
96    /// while handling default and variadic arguments. Assumes check_args() has already succeeded.
97    pub fn bind_args<'a>(&self, args: &'a [Variant], defaults: &'a [Variant], argbuf: &'a mut [Variant]) -> BoundArgs<'a> {
98        debug_assert!(args.len() >= self.required.len());
99        debug_assert!(argbuf.len() == self.arg_len());
100        debug_assert!(defaults.len() == self.default.len());
101        
102        let mut arg_idx = 0;
103        for _ in self.required.iter() {
104            argbuf[arg_idx] = args[arg_idx];
105            arg_idx += 1;
106        }
107        
108        for (default_idx, _) in self.default.iter().enumerate() {
109            if arg_idx < args.len() {
110                argbuf[arg_idx] = args[arg_idx];
111            } else {
112                argbuf[arg_idx] = defaults[default_idx];
113            }
114            arg_idx += 1;
115        }
116        
117        let varargs;
118        if arg_idx > args.len() {
119            varargs = &[] as &[Variant];
120        } else {
121            let (_, rest) = args.split_at(arg_idx);
122            varargs = rest;
123        }
124        
125        BoundArgs {
126            args: argbuf,
127            varargs,
128        }
129    }
130}
131
132pub struct BoundArgs<'a> {
133    pub args: &'a [Variant],
134    pub varargs: &'a [Variant],
135}
136
137
138#[derive(Clone, Debug)]
139pub struct Parameter {
140    name: StringSymbol,
141    mode: Access,
142}
143
144impl Parameter {
145    pub fn new(name: impl Into<StringSymbol>, mode: Access) -> Self {
146        Self { name: name.into(), mode }
147    }
148    
149    pub fn name(&self) -> &StringSymbol { &self.name }
150    pub fn mode(&self) -> &Access { &self.mode }
151}
152
153
154impl fmt::Display for Signature {
155    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
156        fmt.write_str(&self.display)
157    }
158}
159
160fn format_signature(name: Option<&StringSymbol>, required: &[Parameter], default: &[Parameter], variadic: Option<&Parameter>) -> String {
161    STRING_TABLE.with(|string_table| {
162        let string_table = string_table.borrow();
163        
164        let name = name
165            .map(|name| string_table.resolve(name));
166        
167        let mut parameters = Vec::new();
168        
169        let required_names = required.iter()
170            .map(|param| string_table.resolve(&param.name).to_string());
171        parameters.extend(required_names);
172            
173        let default_names = default.iter()
174            .map(|param| string_table.resolve(&param.name))
175            .map(|name| format!("{} = ...", name));
176        parameters.extend(default_names);
177        
178        let variadic_name = variadic
179            .map(|param| string_table.resolve(&param.name))
180            .map(|name| format!("{}...", name));
181        parameters.extend(variadic_name);
182        
183        format!("fun {}({})", name.unwrap_or(""), parameters.join(", "))
184    })
185}