1pub(crate) mod functions;
2pub(crate) mod object;
3
4pub use functions::FunctionDefinition;
5
6use crate::{
7 error::{EggError, EggResult},
8 expression::Value,
9};
10use alloc::{collections::BTreeMap, format};
11use arcstr::ArcStr;
12use core::f32::consts;
13
14#[derive(Debug)]
38#[allow(private_interfaces)]
39pub enum Scope {
40 Global { source: BTreeMap<ArcStr, Value>, extras: ScopeExtras },
41 Local { overlay: BTreeMap<ArcStr, Value>, source: *mut Scope },
42}
43
44impl Default for Scope {
45 fn default() -> Scope {
46 let mut source = BTreeMap::new();
47
48 source.insert("true".into(), true.into());
49 source.insert("false".into(), false.into());
50
51 source.insert("Number".into(), Value::String(arcstr::literal!("__TYPE__NUMBER")));
53 source.insert("String".into(), Value::String(arcstr::literal!("__TYPE__STRING")));
54 source.insert("Nil".into(), Value::String(arcstr::literal!("__CONSTANT__NIL")));
55 source.insert("Boolean".into(), Value::String(arcstr::literal!("__TYPE__BOOLEAN")));
56 source.insert("Function".into(), Value::String(arcstr::literal!("__TYPE__FUNCTION")));
57 source.insert("Object".into(), Value::String(arcstr::literal!("__TYPE__OBJECT")));
58
59 source.insert("PI".into(), consts::PI.into());
61 source.insert("TAU".into(), consts::TAU.into());
62 source.insert("E".into(), consts::E.into());
63
64 Scope::Global { source, extras: Default::default() }
65 }
66}
67
68impl Scope {
69 pub fn exists(&self, key: &str) -> bool {
71 match self {
72 Scope::Global { source, .. } => source.contains_key(key),
73 Scope::Local { overlay, source: parent, .. } => overlay.contains_key(key) || unsafe { parent.as_ref().map(|p| p.exists(key)).unwrap_or(false) },
74 }
75 }
76
77 pub fn exists_locally(&self, key: &str) -> bool {
80 match self {
81 Scope::Global { source, .. } => source.contains_key(key),
82 Scope::Local { overlay, .. } => overlay.contains_key(key),
83 }
84 }
85 pub fn get(&self, key: &str) -> Option<&Value> {
87 match self {
88 Scope::Global { source, .. } => source.get(key),
89 Scope::Local { overlay, source: parent, .. } => overlay.get(key).or_else(|| unsafe { parent.as_mut().and_then(|p| p.get(key)) }),
90 }
91 }
92
93 pub fn get_mut(&mut self, key: &str) -> Option<&mut Value> {
95 match self {
96 Scope::Global { source, .. } => source.get_mut(key),
97 Scope::Local { overlay, source: parent, .. } => overlay.get_mut(key).or_else(|| unsafe { parent.as_mut().and_then(|p| p.get_mut(key)) }),
98 }
99 }
100
101 pub fn insert(&mut self, key: ArcStr, value: Value) -> EggResult<()> {
103 let exists_locally = self.exists_locally(&key);
104
105 match self {
106 Scope::Global { source, .. } => {
107 if exists_locally {
108 return Err(EggError::OperatorComplaint(format!("Variable {} already defined in Global Scope", key)));
109 }
110 source.insert(key, value);
111 }
112 Scope::Local { overlay, .. } => {
113 if exists_locally {
114 return Err(EggError::OperatorComplaint(format!("Variable {} already defined in Global Scope", key)));
115 }
116 overlay.insert(key, value);
117 }
118 };
119
120 Ok(())
121 }
122
123 pub fn update(&mut self, key: ArcStr, value: Value) {
125 let was_local = matches!(self, Scope::Local { overlay, .. } if overlay.contains_key(&key));
126 self.delete(&key);
127
128 match self {
129 Scope::Global { source, .. } => {
130 source.insert(key, value);
131 }
132 Scope::Local { overlay, source } => {
133 if was_local {
134 overlay.insert(key, value);
135 } else {
136 unsafe { source.as_mut().map(|s| s.insert(key, value)) };
137 }
138 }
139 };
140 }
141
142 pub fn delete(&mut self, key: &str) -> Option<Value> {
144 if let Some(Value::Function(index)) = self.get(key) {
145 self.delete_function(*index);
146 }
147
148 if let Some(Value::Object(tag)) = self.get(key) {
149 self.delete_object(*tag);
150 }
151
152 match self {
153 Scope::Global { source, .. } => source.remove(key),
154 Scope::Local { overlay, source: parent, .. } => unsafe { overlay.remove(key).or_else(|| parent.as_mut().and_then(|p| p.delete(key))) },
155 }
156 }
157
158 pub(crate) fn overlay(&mut self, overlay: BTreeMap<ArcStr, Value>) -> Scope {
160 Scope::Local { overlay, source: self as _ }
161 }
162
163 pub(crate) fn extras(&self) -> &ScopeExtras {
165 match self {
166 Scope::Global { extras, .. } => extras,
167 Scope::Local { source, .. } => unsafe { source.as_ref().map(|s| s.extras()).unwrap_unchecked() },
168 }
169 }
170
171 pub(crate) fn extras_mut(&mut self) -> &mut ScopeExtras {
173 match self {
174 Scope::Global { extras, .. } => extras,
175 Scope::Local { source, .. } => unsafe { source.as_mut().map(|s| s.extras_mut()).unwrap_unchecked() },
176 }
177 }
178}
179
180#[derive(Debug, Default)]
181pub(crate) struct ScopeExtras {
182 maps: BTreeMap<usize, BTreeMap<Value, Value>>,
183 functions: BTreeMap<usize, functions::FunctionDefinition>,
184 counter: usize,
185 _unsend: core::marker::PhantomData<*mut ()>,
186}