about summary refs log tree commit diff
path: root/src/interpret/scope.rs
blob: 478b24dc6e5e28c6823c6eee8103c385a4c50303 (plain)
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
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
use super::value::Value;
use crate::parse::ast::nodes::Identifier;
use std::{cell::RefCell, collections::HashMap, rc::Rc};
use thiserror::Error;

pub struct Scope {
    scopes: Vec<HashMap<Identifier, AssignedValue>>,
}

impl Scope {
    pub fn new() -> Self {
        Scope { scopes: Vec::new() }
    }

    pub fn nest(&mut self) {
        self.scopes.push(HashMap::new());
    }

    pub fn unnest(&mut self) {
        self.scopes.pop();
    }

    pub fn set_var(
        &mut self,
        ident: &str,
        value: Value,
        is_constant: bool,
    ) -> Result<(), ScopeError> {
        let wrapped_value = if is_constant {
            AssignedValue::Constant(value)
        } else {
            AssignedValue::Mutable(value)
        };

        let num_scopes = self.scopes.len();
        for scope in self.scopes.iter_mut().take(num_scopes - 1) {
            if let Some(already_assigned_value) = scope.get(ident) {
                if !already_assigned_value.is_constant() {
                    scope.insert(ident.to_string(), wrapped_value);
                    return Ok(());
                } else {
                    return Err(ScopeError::CantReassignConstant(ident.to_string()));
                }
            }
        }

        let inner_scope = self
            .scopes
            .last_mut()
            .expect("Tried accessing scope after last frame is gone.");

        if let Some(already_assigned_value) = inner_scope.get(ident) {
            if !already_assigned_value.is_constant() {
                inner_scope.insert(ident.to_string(), wrapped_value);
                Ok(())
            } else {
                Err(ScopeError::CantReassignConstant(ident.to_string()))
            }
        } else {
            inner_scope.insert(ident.to_string(), wrapped_value);
            Ok(())
        }
    }

    pub fn get_var(&self, ident: &str) -> Result<Value, ScopeError> {
        for scope in self.scopes.iter().rev() {
            if let Some(value) = scope.get(ident) {
                return Ok(value.get_value());
            }
        }
        Err(ScopeError::UnknownIdentifier(ident.to_string()))
    }
}

#[derive(Debug)]
enum AssignedValue {
    Constant(Value),
    Mutable(Value),
}

impl AssignedValue {
    fn get_value(&self) -> Value {
        match self {
            Self::Mutable(value) => value.clone(),
            Self::Constant(value) => match value {
                Value::Array(reference) => {
                    let underlying_value = reference.borrow().clone();
                    Value::Array(Rc::new(RefCell::new(underlying_value)))
                }
                Value::Fn(reference) => {
                    let underlying_value = reference.borrow().clone();
                    Value::Fn(Rc::new(RefCell::new(underlying_value)))
                }
                x => x.clone(),
            },
        }
    }

    fn is_constant(&self) -> bool {
        matches!(self, AssignedValue::Constant(_))
    }
}

#[derive(Error, Debug)]
pub enum ScopeError {
    #[error("Unknown identifier: '{0}'.")]
    UnknownIdentifier(Identifier),
    #[error("Unable to re-assign the constant '{0}'.")]
    CantReassignConstant(Identifier),
}