rooc/runtime_builtin/functions/
function_traits.rs

1use crate::parser::il::PreExp;
2use crate::parser::model_transformer::TransformError;
3use crate::parser::model_transformer::TransformerContext;
4use crate::runtime_builtin::rooc_std::{std_fn_to_latex, std_fn_to_string};
5use crate::traits::{escape_latex, ToLatex};
6use crate::type_checker::type_checker_context::{FunctionContext, TypeCheckerContext};
7use crate::{
8    primitives::{Primitive, PrimitiveKind},
9    type_checker::type_checker_context::{TypeCheckable, WithType},
10    utils::InputSpan,
11};
12use core::fmt;
13use pest::Span;
14use serde::Serialize;
15use std::fmt::Debug;
16
17/// Represents a function call with its arguments, name and source span.
18#[derive(Debug, Clone, Serialize)]
19pub struct FunctionCall {
20    pub args: Vec<PreExp>,
21    pub name: String,
22    span: InputSpan,
23}
24
25impl FunctionCall {
26    /// Creates a new FunctionCall instance.
27    ///
28    /// # Arguments
29    /// * `args` - Vector of PreExp arguments to the function
30    /// * `name` - Name of the function being called
31    /// * `span` - Source code span for error reporting
32    pub fn new(args: Vec<PreExp>, name: String, span: Span) -> Self {
33        Self {
34            args,
35            name,
36            span: InputSpan::from_span(span),
37        }
38    }
39}
40
41/// The default type check implementation, it performs type checking of function arguments against expected types.
42///
43/// # Arguments
44/// * `args` - The actual arguments passed to the function
45/// * `expected` - Vector of expected argument names and their types
46/// * `context` - Type checker context
47/// * `fn_context` - Function context containing function definitions
48///
49/// # Returns
50/// * `Ok(())` if type checking succeeds
51/// * `Err(TransformError)` if there's a type mismatch
52pub fn default_type_check(
53    args: &[PreExp],
54    expected: &[(String, PrimitiveKind)],
55    context: &mut TypeCheckerContext,
56    fn_context: &FunctionContext,
57) -> Result<(), TransformError> {
58    let type_signature = expected;
59    if type_signature.len() != args.len() {
60        return Err(TransformError::WrongNumberOfArguments {
61            signature: type_signature.to_owned(),
62            args: args.to_owned(),
63        });
64    }
65    for (arg, (_, kind)) in args.iter().zip(type_signature) {
66        if kind == &PrimitiveKind::Any {
67            continue;
68        }
69        let arg_type = arg.get_type(context, fn_context);
70        //allow any if they are both iterable and expected is iterable of any
71        if *kind == PrimitiveKind::Iterable(Box::new(PrimitiveKind::Any)) && arg_type.is_iterable()
72        {
73            continue;
74        }
75        //allow anything that can be converted to a number
76        if kind == &PrimitiveKind::Number && arg_type.is_numeric() {
77            continue;
78        }
79        if kind == &PrimitiveKind::Integer {
80            //allow anything that can be converted to a boolean
81            if matches!(
82                arg_type,
83                PrimitiveKind::Integer | PrimitiveKind::PositiveInteger
84            ) {
85                continue;
86            }
87        }
88        if arg_type != *kind {
89            return Err(TransformError::WrongArgument {
90                expected: kind.clone(),
91                got: arg_type,
92            }
93            .add_span(arg.span()));
94        }
95    }
96
97    Ok(())
98}
99
100/// Creates a type error for wrong argument types.
101///
102/// # Arguments
103/// * `args` - The actual arguments passed
104/// * `fun` - The function being called
105/// * `context` - Type checker context
106/// * `fn_context` - Function context
107pub fn default_wrong_type(
108    args: &[PreExp],
109    fun: &dyn RoocFunction,
110    context: &TypeCheckerContext,
111    fn_context: &FunctionContext,
112) -> TransformError {
113    let type_signature = fun.type_signature(args, context, fn_context);
114    let args = args.to_owned();
115    TransformError::WrongFunctionSignature {
116        signature: type_signature,
117        got: args
118            .iter()
119            .map(|a| a.get_type(context, fn_context))
120            .collect(),
121    }
122}
123
124/// Creates an error for wrong number of arguments.
125///
126/// # Arguments
127/// * `fun` - The function being called
128/// * `args` - The arguments passed
129/// * `context` - Type checker context
130/// * `fn_context` - Function context
131pub fn default_wrong_number_of_arguments(
132    fun: &dyn RoocFunction,
133    args: &[PreExp],
134    fn_context: &FunctionContext,
135) -> TransformError {
136    TransformError::WrongNumberOfArguments {
137        signature: fun.type_signature(args, &TypeCheckerContext::default(), fn_context),
138        args: vec![],
139    }
140}
141
142impl TypeCheckable for FunctionCall {
143    fn type_check(
144        &self,
145        context: &mut TypeCheckerContext,
146        fn_context: &FunctionContext,
147    ) -> Result<(), TransformError> {
148        for arg in &self.args {
149            arg.type_check(context, fn_context)
150                .map_err(|e| e.add_span(&self.span))?;
151        }
152        let f = fn_context
153            .function(&self.name)
154            .ok_or_else(|| TransformError::NonExistentFunction(self.name.clone()))?;
155        f.type_check(&self.args, context, fn_context)
156            .map_err(|e| e.add_span(&self.span))
157    }
158
159    fn populate_token_type_map(
160        &self,
161        context: &mut TypeCheckerContext,
162        fn_context: &FunctionContext,
163    ) {
164        self.args
165            .iter()
166            .for_each(|arg| arg.populate_token_type_map(context, fn_context));
167        if let Some(f) = fn_context.function(&self.name) {
168            let return_type = f.return_type(&self.args, context, fn_context);
169            context.add_token_type_or_undefined(return_type, self.span.clone(), None);
170        }
171    }
172}
173
174impl fmt::Display for FunctionCall {
175    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
176        let builtin = std_fn_to_string(self);
177        if let Some(string) = builtin {
178            return write!(f, "{}", string);
179        }
180        write!(f, "{}", default_rooc_function_to_string(self))
181    }
182}
183
184impl ToLatex for FunctionCall {
185    fn to_latex(&self) -> String {
186        //some builtin functions have
187        std_fn_to_latex(self).unwrap_or(default_rooc_function_to_latex(self))
188    }
189}
190
191/// Converts a function call to LaTeX format.
192///
193/// # Arguments
194/// * `function` - The function call to convert
195pub fn default_rooc_function_to_latex(function: &FunctionCall) -> String {
196    format!(
197        "{}({})",
198        escape_latex(&function.name),
199        function
200            .args
201            .iter()
202            .map(|p| p.to_latex())
203            .collect::<Vec<String>>()
204            .join(",\\")
205    )
206}
207
208/// Converts a function call to a string representation.
209///
210/// # Arguments
211/// * `function` - The function call to convert
212pub fn default_rooc_function_to_string(function: &FunctionCall) -> String {
213    format!(
214        "{}({})",
215        function.name,
216        function
217            .args
218            .iter()
219            .map(|p| p.to_string())
220            .collect::<Vec<String>>()
221            .join(", ")
222    )
223}
224
225/// Trait defining the interface for Rooc functions.
226///
227/// This trait must be implemented by all functions that can be called within the Rooc language.
228pub trait RoocFunction: Debug {
229    /// Executes the function with given arguments.
230    ///
231    /// # Arguments
232    /// * `args` - Vector of arguments to the function
233    /// * `context` - Transformer context
234    /// * `fn_context` - Function context
235    ///
236    /// # Returns
237    /// * `Ok(Primitive)` containing the function result
238    /// * `Err(TransformError)` if execution fails
239    fn call(
240        &self,
241        args: &[PreExp],
242        context: &TransformerContext,
243        fn_context: &FunctionContext,
244    ) -> Result<Primitive, TransformError>;
245
246    /// Returns the type signature of the function.
247    fn type_signature(
248        &self,
249        args: &[PreExp],
250        context: &TypeCheckerContext,
251        fn_context: &FunctionContext,
252    ) -> Vec<(String, PrimitiveKind)>;
253
254    /// Determines the return type of the function given its arguments.
255    ///
256    /// # Arguments
257    /// * `args` - The arguments to the function
258    /// * `context` - Type checker context
259    /// * `fn_context` - Function context
260    fn return_type(
261        &self,
262        args: &[PreExp],
263        context: &TypeCheckerContext,
264        fn_context: &FunctionContext,
265    ) -> PrimitiveKind;
266
267    /// Returns the name of the function.
268    fn function_name(&self) -> String;
269
270    /// Type checks the function call.
271    ///
272    /// # Arguments
273    /// * `args` - The arguments to check
274    /// * `context` - Type checker context
275    /// * `fn_context` - Function context
276    fn type_check(
277        &self,
278        args: &[PreExp],
279        context: &mut TypeCheckerContext,
280        fn_context: &FunctionContext,
281    ) -> Result<(), TransformError> {
282        default_type_check(
283            args,
284            &self.type_signature(args, context, fn_context),
285            context,
286            fn_context,
287        )
288    }
289}