1use super::{Caches, EvalContext, GlobalRuntimeState, Target};
4use crate::ast::Expr;
5use crate::packages::string_basic::{print_with_func, FUNC_TO_STRING};
6use crate::types::dynamic::AccessMode;
7use crate::{Dynamic, Engine, RhaiResult, RhaiResultOf, Scope, SmartString, ERR};
8#[cfg(feature = "no_std")]
9use std::prelude::v1::*;
10use std::{fmt::Write, num::NonZeroUsize};
11
12#[cfg(not(feature = "no_module"))]
14#[inline]
15#[must_use]
16pub fn search_imports(
17 engine: &Engine,
18 global: &GlobalRuntimeState,
19 namespace: &crate::ast::Namespace,
20) -> Option<crate::SharedModule> {
21 debug_assert!(!namespace.is_empty());
22
23 let root = namespace.root();
24
25 if !global.always_search_scope {
27 if let Some(index) = namespace.index {
28 let offset = global.num_imports() - index.get();
29
30 if let m @ Some(_) = global.get_shared_import(offset) {
31 return m;
32 }
33 }
34 }
35
36 global.find_import(root).map_or_else(
38 || engine.global_sub_modules.get(root).cloned(),
39 |offset| global.get_shared_import(offset),
40 )
41}
42
43pub fn search_scope_only<'s>(
49 engine: &Engine,
50 global: &mut GlobalRuntimeState,
51 caches: &mut Caches,
52 scope: &'s mut Scope,
53 this_ptr: Option<&'s mut Dynamic>,
54 expr: &Expr,
55) -> RhaiResultOf<Target<'s>> {
56 let index = match expr {
59 Expr::ThisPtr(..) => unreachable!("Expr::ThisPtr should have been handled outside"),
61
62 _ if global.always_search_scope => 0,
63
64 Expr::Variable(_, Some(i), ..) => i.get() as usize,
65 Expr::Variable(v, None, ..) => {
66 #[cfg(not(feature = "no_function"))]
68 if let Some(fn_def) = global
69 .lib
70 .iter()
71 .flat_map(|m| m.iter_script_fn())
72 .find_map(|(_, _, f, _, func)| if f == v.3 { Some(func) } else { None })
73 {
74 let val: Dynamic = crate::FnPtr {
75 name: v.3.clone(),
76 curry: <_>::default(),
77 environ: None,
78 fn_def: Some(fn_def.clone()),
79 }
80 .into();
81 return Ok(val.into());
82 }
83
84 v.0.map_or(0, NonZeroUsize::get)
85 }
86
87 _ => unreachable!("Expr::Variable expected but gets {:?}", expr),
88 };
89
90 if let Some(ref resolve_var) = engine.resolve_var {
92 let orig_scope_len = scope.len();
93
94 let context = EvalContext::new(engine, global, caches, scope, this_ptr);
95 let var_name = expr.get_variable_name(true).unwrap();
96 let resolved_var = resolve_var(var_name, index, context);
97
98 if orig_scope_len != scope.len() {
99 global.always_search_scope = true;
101 }
102
103 match resolved_var {
104 Ok(Some(mut result)) => {
105 result.set_access_mode(AccessMode::ReadOnly);
106 return Ok(result.into());
107 }
108 Ok(None) => (),
109 Err(err) => return Err(err.fill_position(expr.position())),
110 }
111 }
112
113 let index = if index > 0 {
114 scope.len() - index
115 } else {
116 let var_name = expr.get_variable_name(true).unwrap();
118
119 match scope.search(var_name) {
120 Some(index) => index,
121 None => {
122 return engine
123 .global_modules
124 .iter()
125 .find_map(|m| m.get_var(var_name))
126 .map_or_else(
127 || {
128 Err(
129 ERR::ErrorVariableNotFound(var_name.to_string(), expr.position())
130 .into(),
131 )
132 },
133 |val| Ok(val.into()),
134 )
135 }
136 }
137 };
138
139 let val = scope.get_mut_by_index(index);
140
141 Ok(val.into())
142}
143
144pub fn search_namespace<'s>(
147 engine: &Engine,
148 global: &mut GlobalRuntimeState,
149 caches: &mut Caches,
150 scope: &'s mut Scope,
151 this_ptr: Option<&'s mut Dynamic>,
152 expr: &Expr,
153) -> RhaiResultOf<Target<'s>> {
154 match expr {
155 Expr::Variable(_, Some(_), _) => {
156 search_scope_only(engine, global, caches, scope, this_ptr, expr)
157 }
158 Expr::Variable(v, None, ..) => match &**v {
159 (_, ns, ..) if ns.is_empty() => {
161 search_scope_only(engine, global, caches, scope, this_ptr, expr)
162 }
163
164 #[cfg(not(feature = "no_module"))]
166 (_, ns, hash_var, var_name) => {
167 if let Some(module) = search_imports(engine, global, ns) {
169 return module.get_qualified_var(*hash_var).map_or_else(
170 || {
171 let sep = crate::engine::NAMESPACE_SEPARATOR;
172
173 Err(ERR::ErrorVariableNotFound(
174 format!("{ns}{sep}{var_name}"),
175 ns.position(),
176 )
177 .into())
178 },
179 |mut target| {
180 target.set_access_mode(AccessMode::ReadOnly);
182 Ok(target.into())
183 },
184 );
185 }
186
187 #[cfg(not(feature = "no_function"))]
189 if ns.path.len() == 1 && ns.root() == crate::engine::KEYWORD_GLOBAL {
190 if let Some(ref constants) = global.constants {
191 if let Some(value) =
192 crate::func::locked_write(constants).get_mut(var_name.as_str())
193 {
194 let mut target: Target = value.clone().into();
195 target.as_mut().set_access_mode(AccessMode::ReadOnly);
197 return Ok(target);
198 }
199 }
200
201 let sep = crate::engine::NAMESPACE_SEPARATOR;
202
203 return Err(ERR::ErrorVariableNotFound(
204 format!("{ns}{sep}{var_name}"),
205 ns.position(),
206 )
207 .into());
208 }
209
210 Err(ERR::ErrorModuleNotFound(ns.to_string(), ns.position()).into())
211 }
212
213 #[cfg(feature = "no_module")]
214 _ => unreachable!("Invalid expression {:?}", expr),
215 },
216 _ => unreachable!("Expr::Variable expected but gets {:?}", expr),
217 }
218}
219
220impl Engine {
221 pub(crate) fn eval_expr(
223 &self,
224 global: &mut GlobalRuntimeState,
225 caches: &mut Caches,
226 scope: &mut Scope,
227 mut this_ptr: Option<&mut Dynamic>,
228 expr: &Expr,
229 ) -> RhaiResult {
230 self.track_operation(global, expr.position())?;
231
232 #[cfg(feature = "debugging")]
233 let reset =
234 self.run_debugger_with_reset(global, caches, scope, this_ptr.as_deref_mut(), expr)?;
235 #[cfg(feature = "debugging")]
236 defer! { global if Some(reset) => move |g| g.debugger_mut().reset_status(reset) }
237
238 match expr {
239 Expr::IntegerConstant(x, ..) => Ok((*x).into()),
241 Expr::StringConstant(x, ..) => Ok(x.clone().into()),
242 Expr::BoolConstant(x, ..) => Ok((*x).into()),
243 #[cfg(not(feature = "no_float"))]
244 Expr::FloatConstant(x, ..) => Ok((*x).into()),
245 Expr::CharConstant(x, ..) => Ok((*x).into()),
246 Expr::Unit(..) => Ok(Dynamic::UNIT),
247 Expr::DynamicConstant(x, ..) => Ok(x.as_ref().clone()),
248
249 Expr::FnCall(x, pos) => {
250 self.eval_fn_call_expr(global, caches, scope, this_ptr, x, *pos)
251 }
252
253 Expr::ThisPtr(var_pos) => this_ptr
254 .ok_or_else(|| ERR::ErrorUnboundThis(*var_pos).into())
255 .cloned(),
256
257 Expr::Variable(..) => search_namespace(self, global, caches, scope, this_ptr, expr)
258 .map(Target::take_or_clone),
259
260 Expr::InterpolatedString(x, _) => {
261 let mut concat = SmartString::new_const();
262
263 for expr in &**x {
264 let item = &mut self
265 .eval_expr(global, caches, scope, this_ptr.as_deref_mut(), expr)?
266 .flatten();
267 let pos = expr.position();
268
269 if item.is_string() {
270 write!(concat, "{item}").unwrap();
271 } else {
272 let source = global.source();
273 let context = &(self, FUNC_TO_STRING, source, &*global, pos).into();
274 let display = print_with_func(FUNC_TO_STRING, context, item);
275 write!(concat, "{display}").unwrap();
276 }
277
278 #[cfg(not(feature = "unchecked"))]
279 self.throw_on_size((0, 0, concat.len()))
280 .map_err(|err| err.fill_position(pos))?;
281 }
282
283 Ok(self.get_interned_string(concat).into())
284 }
285
286 #[cfg(not(feature = "no_index"))]
287 Expr::Array(x, ..) => {
288 let mut array = crate::Array::with_capacity(x.len());
289
290 #[cfg(not(feature = "unchecked"))]
291 let mut total_data_sizes = (0, 0, 0);
292
293 for item_expr in &**x {
294 let value = self
295 .eval_expr(global, caches, scope, this_ptr.as_deref_mut(), item_expr)?
296 .flatten();
297
298 #[cfg(not(feature = "unchecked"))]
299 if self.has_data_size_limit() {
300 let val_sizes = crate::eval::calc_data_sizes(&value, true);
301
302 total_data_sizes = (
303 total_data_sizes.0 + val_sizes.0 + 1,
304 total_data_sizes.1 + val_sizes.1,
305 total_data_sizes.2 + val_sizes.2,
306 );
307 self.throw_on_size(total_data_sizes)
308 .map_err(|err| err.fill_position(item_expr.position()))?;
309 }
310
311 array.push(value);
312 }
313
314 Ok(Dynamic::from_array(array))
315 }
316
317 #[cfg(not(feature = "no_object"))]
318 #[cfg(not(feature = "indexmap"))]
319 Expr::Map(x, ..) => {
320 let mut map = x.1.clone();
321
322 #[cfg(not(feature = "unchecked"))]
323 let mut total_data_sizes = (0, 0, 0);
324
325 for (key, value_expr) in &x.0 {
326 let value = self
327 .eval_expr(global, caches, scope, this_ptr.as_deref_mut(), value_expr)?
328 .flatten();
329
330 #[cfg(not(feature = "unchecked"))]
331 if self.has_data_size_limit() {
332 let delta = crate::eval::calc_data_sizes(&value, true);
333 total_data_sizes = (
334 total_data_sizes.0 + delta.0,
335 total_data_sizes.1 + delta.1 + 1,
336 total_data_sizes.2 + delta.2,
337 );
338 self.throw_on_size(total_data_sizes)
339 .map_err(|err| err.fill_position(value_expr.position()))?;
340 }
341
342 *map.get_mut(key.as_str()).unwrap() = value;
343 }
344
345 Ok(Dynamic::from_map(map))
346 }
347
348 #[cfg(not(feature = "no_object"))]
349 #[cfg(feature = "indexmap")]
350 Expr::Map(x, ..) => {
351 let mut map = crate::Map::with_capacity(x.0.len());
352
353 #[cfg(not(feature = "unchecked"))]
354 let mut total_data_sizes = (0, 0, 0);
355
356 for (key, value_expr) in &x.0 {
357 let value = self
358 .eval_expr(global, caches, scope, this_ptr.as_deref_mut(), value_expr)?
359 .flatten();
360
361 #[cfg(not(feature = "unchecked"))]
362 if self.has_data_size_limit() {
363 let delta = crate::eval::calc_data_sizes(&value, true);
364 total_data_sizes = (
365 total_data_sizes.0 + delta.0,
366 total_data_sizes.1 + delta.1 + 1,
367 total_data_sizes.2 + delta.2,
368 );
369 self.throw_on_size(total_data_sizes)
370 .map_err(|err| err.fill_position(value_expr.position()))?;
371 }
372
373 map.insert(key.as_str().into(), value);
374 }
375
376 Ok(Dynamic::from_map(map))
377 }
378
379 Expr::And(x, ..) => Ok((self
380 .eval_expr(global, caches, scope, this_ptr.as_deref_mut(), &x.lhs)?
381 .as_bool()
382 .map_err(|typ| self.make_type_mismatch_err::<bool>(typ, x.lhs.position()))?
383 && self
384 .eval_expr(global, caches, scope, this_ptr, &x.rhs)?
385 .as_bool()
386 .map_err(|typ| self.make_type_mismatch_err::<bool>(typ, x.rhs.position()))?)
387 .into()),
388
389 Expr::Or(x, ..) => Ok((self
390 .eval_expr(global, caches, scope, this_ptr.as_deref_mut(), &x.lhs)?
391 .as_bool()
392 .map_err(|typ| self.make_type_mismatch_err::<bool>(typ, x.lhs.position()))?
393 || self
394 .eval_expr(global, caches, scope, this_ptr, &x.rhs)?
395 .as_bool()
396 .map_err(|typ| self.make_type_mismatch_err::<bool>(typ, x.rhs.position()))?)
397 .into()),
398
399 Expr::Coalesce(x, ..) => {
400 let value =
401 self.eval_expr(global, caches, scope, this_ptr.as_deref_mut(), &x.lhs)?;
402
403 if value.is_unit() {
404 self.eval_expr(global, caches, scope, this_ptr, &x.rhs)
405 } else {
406 Ok(value)
407 }
408 }
409
410 #[cfg(not(feature = "no_custom_syntax"))]
411 Expr::Custom(custom, pos) => {
412 let expressions: crate::StaticVec<_> =
413 custom.inputs.iter().map(Into::into).collect();
414 let key_token = custom.tokens.first().unwrap();
416 let custom_def = self.custom_syntax.get(key_token.as_str()).ok_or_else(|| {
418 Box::new(ERR::ErrorCustomSyntax(
419 format!("Invalid custom syntax prefix: {key_token}"),
420 custom.tokens.iter().map(<_>::to_string).collect(),
421 *pos,
422 ))
423 })?;
424 let mut context = EvalContext::new(self, global, caches, scope, this_ptr);
425
426 (custom_def.func)(&mut context, &expressions, &custom.state)
427 .and_then(|r| self.check_data_size(r, expr.start_position()))
428 }
429
430 Expr::Stmt(x) => {
431 self.eval_stmt_block(global, caches, scope, this_ptr, x.statements(), true)
432 }
433
434 #[cfg(not(feature = "no_index"))]
435 Expr::Index(..) => {
436 self.eval_dot_index_chain(global, caches, scope, this_ptr, expr, None)
437 }
438
439 #[cfg(not(feature = "no_object"))]
440 Expr::Dot(..) => self.eval_dot_index_chain(global, caches, scope, this_ptr, expr, None),
441
442 #[allow(unreachable_patterns)]
443 _ => unreachable!("expression cannot be evaluated: {:?}", expr),
444 }
445 }
446}