roan_engine/interpreter/
functions.rs

1use crate::{
2    context::Context,
3    module::{Module, StoredFunction},
4    value::Value,
5    vm::{native_fn::NativeFunction, VM},
6};
7use anyhow::Result;
8use roan_ast::{CallExpr, GetSpan};
9use roan_error::{
10    error::RoanError::{MissingParameter, TypeMismatch, UndefinedFunctionError},
11    frame::Frame,
12    print_diagnostic, TextSpan,
13};
14use tracing::debug;
15
16impl Module {
17    /// Executes a native function with the provided arguments.
18    pub fn execute_native_function(
19        &mut self,
20        mut native: NativeFunction,
21        args: Vec<Value>,
22        vm: &mut VM,
23    ) -> Result<()> {
24        let result = native.call(args)?;
25        vm.push(result);
26
27        Ok(())
28    }
29
30    /// Executes a user-defined function with the provided arguments.
31    pub fn execute_user_defined_function(
32        &mut self,
33        function: roan_ast::Fn,
34        def_module: &mut Module,
35        args: Vec<Value>,
36        ctx: &mut Context,
37        vm: &mut VM,
38        call: &CallExpr,
39    ) -> Result<()> {
40        debug!("Executing user-defined function: {}", function.name);
41
42        self.enter_scope();
43
44        {
45            let exprs = call
46                .args
47                .iter()
48                .map(|arg| arg.span())
49                .collect::<Vec<TextSpan>>();
50
51            let mut offset = 0;
52            for (i, (param, arg)) in function
53                .params
54                .iter()
55                .zip(args.iter().chain(std::iter::repeat(&Value::Null)))
56                .enumerate()
57            {
58                let ident = param.ident.literal();
59                // Maybe we could find a better way to handle this
60                if ident == "self" {
61                    offset += 1;
62
63                    def_module.declare_variable(ident, arg.clone());
64                    continue;
65                }
66
67                let expr = exprs.get(i - offset);
68                if param.is_rest {
69                    let rest: Vec<Value> = args
70                        .iter()
71                        .skip(function.params.len() - 1)
72                        .cloned()
73                        .collect();
74
75                    if expr.is_none() {
76                        def_module.declare_variable(ident, Value::Vec(rest));
77                    } else {
78                        if let Some(_type) = param.type_annotation.as_ref() {
79                            for arg in &rest {
80                                if _type.is_any() {
81                                    continue;
82                                };
83
84                                arg.check_type(&_type.type_name.literal(), expr.unwrap().clone())?;
85                            }
86                        }
87
88                        def_module.declare_variable(ident, Value::Vec(rest));
89                    }
90                } else {
91                    if let Some(_type) = param.type_annotation.as_ref() {
92                        if arg.is_null() && _type.is_nullable {
93                            def_module.declare_variable(ident, Value::Null);
94                            continue;
95                        }
96
97                        if expr.is_none() {
98                            return Err(MissingParameter(ident.clone(), call.span()).into());
99                        }
100
101                        if arg.is_null() && !_type.is_nullable {
102                            return Err(TypeMismatch(
103                                format!("Expected type {} but got null", _type.type_name.literal()),
104                                expr.unwrap().clone(),
105                            )
106                            .into());
107                        }
108
109                        if _type.is_array {
110                            match arg {
111                                Value::Vec(vec) => {
112                                    for arg in vec {
113                                        if _type.is_any() {
114                                            continue;
115                                        };
116
117                                        arg.check_type(
118                                            &_type.type_name.literal(),
119                                            expr.unwrap().clone(),
120                                        )?;
121                                    }
122                                    def_module.declare_variable(ident.clone(), arg.clone());
123                                }
124                                _ => {
125                                    return Err(TypeMismatch(
126                                        format!(
127                                            "Expected array type {} but got {}",
128                                            _type.type_name.literal(),
129                                            arg.type_name()
130                                        ),
131                                        expr.unwrap().clone(),
132                                    )
133                                    .into());
134                                }
135                            }
136                        } else {
137                            if arg.is_null() && !_type.is_nullable && !_type.is_any() {
138                                arg.check_type(&_type.type_name.literal(), expr.unwrap().clone())?;
139                            }
140                            def_module.declare_variable(ident, arg.clone());
141                        }
142                    } else {
143                        def_module.declare_variable(ident, arg.clone());
144                    }
145                }
146            }
147        }
148
149        let frame = Frame::new(
150            function.name.clone(),
151            function.fn_token.span.clone(),
152            Frame::path_or_unknown(def_module.path()),
153        );
154        vm.push_frame(frame);
155
156        for stmt in function.body.stmts {
157            def_module.interpret_stmt(stmt, ctx, vm)?;
158        }
159
160        vm.pop_frame();
161        self.exit_scope();
162
163        Ok(())
164    }
165
166    /// Interpret a call expression.
167    ///
168    /// # Arguments
169    /// * `call` - [CallExpr] to interpret.
170    /// * `ctx` - The context in which to interpret the call.
171    ///
172    /// # Returns
173    /// The result of the call.
174    pub fn interpret_call(
175        &mut self,
176        call: &CallExpr,
177        ctx: &mut Context,
178        vm: &mut VM,
179    ) -> Result<Value> {
180        log::debug!("Interpreting call");
181
182        let args = self.interpret_possible_spread(call.args.clone(), ctx, vm)?;
183
184        let stored_function = self
185            .find_function(&call.callee)
186            .ok_or_else(|| UndefinedFunctionError(call.callee.clone(), call.token.span.clone()))?
187            .clone();
188
189        match stored_function {
190            StoredFunction::Native(n) => {
191                self.execute_native_function(n, args, vm)?;
192
193                Ok(vm.pop().unwrap())
194            }
195            StoredFunction::Function {
196                function,
197                defining_module,
198            } => {
199                let mut def_module = ctx.query_module(&defining_module).unwrap();
200
201                match self.execute_user_defined_function(
202                    function,
203                    &mut def_module,
204                    args,
205                    ctx,
206                    vm,
207                    call,
208                ) {
209                    Ok(_) => Ok(vm.pop().unwrap_or(Value::Void)),
210                    Err(e) => {
211                        print_diagnostic(e, Some(def_module.source.content()));
212                        std::process::exit(1);
213                    }
214                }
215            }
216        }
217    }
218}