cl_interpret/
function.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
//! Represents a block of code which lives inside the Interpreter

use collect_upvars::collect_upvars;

use super::{Callable, ConValue, Environment, Error, IResult, Interpret};
use cl_ast::{Function as FnDecl, Param, Sym};
use std::{
    cell::{Ref, RefCell},
    collections::HashMap,
    rc::Rc,
};

pub mod collect_upvars;

type Upvars = HashMap<Sym, Option<ConValue>>;

/// Represents a block of code which persists inside the Interpreter
#[derive(Clone, Debug)]
pub struct Function {
    /// Stores the contents of the function declaration
    decl: Rc<FnDecl>,
    /// Stores data from the enclosing scopes
    upvars: RefCell<Upvars>,
}

impl Function {
    pub fn new(decl: &FnDecl) -> Self {
        // let upvars = collect_upvars(decl, env);
        Self { decl: decl.clone().into(), upvars: Default::default() }
    }
    pub fn decl(&self) -> &FnDecl {
        &self.decl
    }
    pub fn upvars(&self) -> Ref<Upvars> {
        self.upvars.borrow()
    }
    pub fn lift_upvars(&self, env: &Environment) {
        let upvars = collect_upvars(&self.decl, env);
        if let Ok(mut self_upvars) = self.upvars.try_borrow_mut() {
            *self_upvars = upvars;
        }
    }
}

impl Callable for Function {
    fn name(&self) -> Sym {
        let FnDecl { name, .. } = *self.decl;
        name
    }
    fn call(&self, env: &mut Environment, args: &[ConValue]) -> IResult<ConValue> {
        let FnDecl { name, bind, body, sign: _ } = &*self.decl;

        // Check arg mapping
        if args.len() != bind.len() {
            return Err(Error::ArgNumber { want: bind.len(), got: args.len() });
        }
        let Some(body) = body else {
            return Err(Error::NotDefined(*name));
        };

        let upvars = self.upvars.take();
        env.push_frame("upvars", upvars);

        // TODO: completely refactor data storage
        let mut frame = env.frame("fn args");
        for (Param { mutability: _, name }, value) in bind.iter().zip(args) {
            frame.insert(*name, Some(value.clone()));
        }
        let res = body.interpret(&mut frame);
        drop(frame);
        if let Some((upvars, _)) = env.pop_frame() {
            self.upvars.replace(upvars);
        }
        match res {
            Err(Error::Return(value)) => Ok(value),
            Err(Error::Break(value)) => Err(Error::BadBreak(value)),
            result => result,
        }
    }
}