Favor matching on Union's instead of downcast::<T>, as_xxx() or is::<T>.
This commit is contained in:
parent
c799a4567a
commit
1ace4b474c
34
src/any.rs
34
src/any.rs
@ -263,8 +263,24 @@ fn cast_box<X: Variant, T: Variant>(item: Box<X>) -> Result<T, Box<X>> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Dynamic {
|
impl Dynamic {
|
||||||
|
/// Get a reference to the inner `Union`.
|
||||||
|
pub(crate) fn get_ref(&self) -> &Union {
|
||||||
|
&self.0
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get a mutable reference to the inner `Union`.
|
||||||
|
pub(crate) fn get_mut(&mut self) -> &mut Union {
|
||||||
|
&mut self.0
|
||||||
|
}
|
||||||
|
|
||||||
/// Create a `Dynamic` from any type. A `Dynamic` value is simply returned as is.
|
/// Create a `Dynamic` from any type. A `Dynamic` value is simply returned as is.
|
||||||
///
|
///
|
||||||
|
/// Beware that you need to pass in an `Array` type for it to be recognized as an `Array`.
|
||||||
|
/// A `Vec<T>` does not get automatically converted to an `Array`, but will be a generic
|
||||||
|
/// restricted trait object instead, because `Vec<T>` is not a supported standard type.
|
||||||
|
///
|
||||||
|
/// Similarly, passing in a `HashMap<String, T>` will not get a `Map` but a trait object.
|
||||||
|
///
|
||||||
/// # Examples
|
/// # Examples
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
@ -466,24 +482,6 @@ impl Dynamic {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Cast the `Dynamic` as an `Array` and return a reference to it.
|
|
||||||
/// Returns the name of the actual type if the cast fails.
|
|
||||||
pub(crate) fn as_array(&self) -> Result<&Array, &'static str> {
|
|
||||||
match &self.0 {
|
|
||||||
Union::Array(array) => Ok(array),
|
|
||||||
_ => Err(self.type_name()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Cast the `Dynamic` as a `Map` and return a reference to it.
|
|
||||||
/// Returns the name of the actual type if the cast fails.
|
|
||||||
pub(crate) fn as_map(&self) -> Result<&Map, &'static str> {
|
|
||||||
match &self.0 {
|
|
||||||
Union::Map(map) => Ok(map),
|
|
||||||
_ => Err(self.type_name()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn from_unit() -> Self {
|
pub(crate) fn from_unit() -> Self {
|
||||||
Self(Union::Unit(()))
|
Self(Union::Unit(()))
|
||||||
}
|
}
|
||||||
|
279
src/engine.rs
279
src/engine.rs
@ -94,12 +94,6 @@ enum Target<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Target<'a> {
|
impl<'a> Target<'a> {
|
||||||
fn from(value: &'a mut Dynamic) -> Self {
|
|
||||||
Self::Value(value)
|
|
||||||
}
|
|
||||||
fn from_src(src: ScopeSource<'a>) -> Self {
|
|
||||||
Self::Scope(src)
|
|
||||||
}
|
|
||||||
fn get_mut(self, scope: &'a mut Scope) -> &'a mut Dynamic {
|
fn get_mut(self, scope: &'a mut Scope) -> &'a mut Dynamic {
|
||||||
match self {
|
match self {
|
||||||
Self::Value(t) => t,
|
Self::Value(t) => t,
|
||||||
@ -108,6 +102,18 @@ impl<'a> Target<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<'a> From<ScopeSource<'a>> for Target<'a> {
|
||||||
|
fn from(src: ScopeSource<'a>) -> Self {
|
||||||
|
Self::Scope(src)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> From<&'a mut Dynamic> for Target<'a> {
|
||||||
|
fn from(value: &'a mut Dynamic) -> Self {
|
||||||
|
Self::Value(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Eq, PartialEq, Hash, Clone)]
|
#[derive(Debug, Eq, PartialEq, Hash, Clone)]
|
||||||
pub struct FnSpec<'a> {
|
pub struct FnSpec<'a> {
|
||||||
pub name: Cow<'a, str>,
|
pub name: Cow<'a, str>,
|
||||||
@ -497,35 +503,37 @@ impl Engine<'_> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if let Some(prop) = extract_prop_from_getter(fn_name) {
|
if let Some(prop) = extract_prop_from_getter(fn_name) {
|
||||||
// Map property access
|
return match args[0] {
|
||||||
if let Ok(map) = args[0].as_map() {
|
// Map property access
|
||||||
return Ok(map
|
Dynamic(Union::Map(map)) => Ok(map
|
||||||
.get(prop)
|
.get(prop)
|
||||||
.cloned()
|
.cloned()
|
||||||
.unwrap_or_else(|| Dynamic::from_unit()));
|
.unwrap_or_else(|| Dynamic::from_unit())),
|
||||||
}
|
|
||||||
|
|
||||||
// Getter function not found
|
// Getter function not found
|
||||||
return Err(EvalAltResult::ErrorDotExpr(
|
_ => Err(EvalAltResult::ErrorDotExpr(
|
||||||
format!("- property '{}' unknown or write-only", prop),
|
format!("- property '{}' unknown or write-only", prop),
|
||||||
pos,
|
pos,
|
||||||
));
|
)),
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(prop) = extract_prop_from_setter(fn_name) {
|
if let Some(prop) = extract_prop_from_setter(fn_name) {
|
||||||
let value = args[1].clone();
|
let value = args[1].clone();
|
||||||
|
|
||||||
// Map property update
|
return match args[0] {
|
||||||
if let Dynamic(Union::Map(map)) = args[0] {
|
// Map property update
|
||||||
map.insert(prop.to_string(), value);
|
Dynamic(Union::Map(map)) => {
|
||||||
return Ok(Dynamic::from_unit());
|
map.insert(prop.to_string(), value);
|
||||||
}
|
Ok(Dynamic::from_unit())
|
||||||
|
}
|
||||||
|
|
||||||
// Setter function not found
|
// Setter function not found
|
||||||
return Err(EvalAltResult::ErrorDotExpr(
|
_ => Err(EvalAltResult::ErrorDotExpr(
|
||||||
format!("- property '{}' unknown or read-only", prop),
|
format!("- property '{}' unknown or read-only", prop),
|
||||||
pos,
|
pos,
|
||||||
));
|
)),
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(val) = def_val {
|
if let Some(val) = def_val {
|
||||||
@ -610,8 +618,7 @@ impl Engine<'_> {
|
|||||||
let mut args = [target.get_mut(scope)];
|
let mut args = [target.get_mut(scope)];
|
||||||
self.call_fn_raw(None, fn_lib, &make_getter(id), &mut args, None, *pos, 0)
|
self.call_fn_raw(None, fn_lib, &make_getter(id), &mut args, None, *pos, 0)
|
||||||
.and_then(|mut val| {
|
.and_then(|mut val| {
|
||||||
let target = Target::from(&mut val);
|
self.get_dot_val_helper(scope, fn_lib, (&mut val).into(), rhs, level)
|
||||||
self.get_dot_val_helper(scope, fn_lib, target, rhs, level)
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
// xxx.idx_lhs[idx_expr].rhs
|
// xxx.idx_lhs[idx_expr].rhs
|
||||||
@ -638,8 +645,7 @@ impl Engine<'_> {
|
|||||||
|
|
||||||
self.get_indexed_value(scope, fn_lib, &val, idx_expr, *op_pos, level)
|
self.get_indexed_value(scope, fn_lib, &val, idx_expr, *op_pos, level)
|
||||||
.and_then(|(mut val, _)| {
|
.and_then(|(mut val, _)| {
|
||||||
let target = Target::from(&mut val);
|
self.get_dot_val_helper(scope, fn_lib, (&mut val).into(), rhs, level)
|
||||||
self.get_dot_val_helper(scope, fn_lib, target, rhs, level)
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
// Syntax error
|
// Syntax error
|
||||||
@ -676,34 +682,34 @@ impl Engine<'_> {
|
|||||||
|
|
||||||
// This is a variable property access (potential function call).
|
// This is a variable property access (potential function call).
|
||||||
// Use a direct index into `scope` to directly mutate the variable value.
|
// Use a direct index into `scope` to directly mutate the variable value.
|
||||||
self.get_dot_val_helper(scope, fn_lib, Target::from_src(entry), dot_rhs, level)
|
self.get_dot_val_helper(scope, fn_lib, entry.into(), dot_rhs, level)
|
||||||
}
|
}
|
||||||
|
|
||||||
// idx_lhs[idx_expr].???
|
// idx_lhs[idx_expr].???
|
||||||
Expr::Index(idx_lhs, idx_expr, op_pos) => {
|
Expr::Index(idx_lhs, idx_expr, op_pos) => {
|
||||||
let (src, index, mut val) =
|
let (src, index, mut val) =
|
||||||
self.eval_index_expr(scope, fn_lib, idx_lhs, idx_expr, *op_pos, level)?;
|
self.eval_index_expr(scope, fn_lib, idx_lhs, idx_expr, *op_pos, level)?;
|
||||||
let target = Target::from(&mut val);
|
let value =
|
||||||
let value = self.get_dot_val_helper(scope, fn_lib, target, dot_rhs, level);
|
self.get_dot_val_helper(scope, fn_lib, (&mut val).into(), dot_rhs, level);
|
||||||
|
|
||||||
// In case the expression mutated `target`, we need to update it back into the scope because it is cloned.
|
// In case the expression mutated `target`, we need to update it back into the scope because it is cloned.
|
||||||
match src.map(|s| s.typ) {
|
if let Some(src) = src {
|
||||||
None => (),
|
match src.typ {
|
||||||
|
ScopeEntryType::Constant => {
|
||||||
|
return Err(EvalAltResult::ErrorAssignmentToConstant(
|
||||||
|
src.name.to_string(),
|
||||||
|
idx_lhs.position(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
Some(ScopeEntryType::Constant) => {
|
ScopeEntryType::Normal => {
|
||||||
return Err(EvalAltResult::ErrorAssignmentToConstant(
|
Self::update_indexed_var_in_scope(
|
||||||
src.unwrap().name.to_string(),
|
scope,
|
||||||
idx_lhs.position(),
|
src,
|
||||||
));
|
index,
|
||||||
}
|
(val, dot_rhs.position()),
|
||||||
|
)?;
|
||||||
Some(ScopeEntryType::Normal) => {
|
}
|
||||||
Self::update_indexed_var_in_scope(
|
|
||||||
scope,
|
|
||||||
src.unwrap(),
|
|
||||||
index,
|
|
||||||
(val, dot_rhs.position()),
|
|
||||||
)?;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -713,7 +719,7 @@ impl Engine<'_> {
|
|||||||
// {expr}.???
|
// {expr}.???
|
||||||
expr => {
|
expr => {
|
||||||
let mut val = self.eval_expr(scope, fn_lib, expr, level)?;
|
let mut val = self.eval_expr(scope, fn_lib, expr, level)?;
|
||||||
self.get_dot_val_helper(scope, fn_lib, Target::from(&mut val), dot_rhs, level)
|
self.get_dot_val_helper(scope, fn_lib, (&mut val).into(), dot_rhs, level)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -740,66 +746,69 @@ impl Engine<'_> {
|
|||||||
level: usize,
|
level: usize,
|
||||||
) -> Result<(Dynamic, IndexValue), EvalAltResult> {
|
) -> Result<(Dynamic, IndexValue), EvalAltResult> {
|
||||||
let idx_pos = idx_expr.position();
|
let idx_pos = idx_expr.position();
|
||||||
|
let type_name = self.map_type_name(val.type_name());
|
||||||
|
|
||||||
// val_array[idx]
|
match val.get_ref() {
|
||||||
if let Ok(arr) = val.as_array() {
|
Union::Array(arr) => {
|
||||||
let index = self
|
// val_array[idx]
|
||||||
.eval_expr(scope, fn_lib, idx_expr, level)?
|
let index = self
|
||||||
.as_int()
|
.eval_expr(scope, fn_lib, idx_expr, level)?
|
||||||
.map_err(|_| EvalAltResult::ErrorNumericIndexExpr(idx_expr.position()))?;
|
.as_int()
|
||||||
|
.map_err(|_| EvalAltResult::ErrorNumericIndexExpr(idx_expr.position()))?;
|
||||||
|
|
||||||
return if index >= 0 {
|
return if index >= 0 {
|
||||||
arr.get(index as usize)
|
arr.get(index as usize)
|
||||||
.map(|v| (v.clone(), IndexValue::from_num(index)))
|
.map(|v| (v.clone(), IndexValue::from_num(index)))
|
||||||
.ok_or_else(|| EvalAltResult::ErrorArrayBounds(arr.len(), index, idx_pos))
|
.ok_or_else(|| EvalAltResult::ErrorArrayBounds(arr.len(), index, idx_pos))
|
||||||
} else {
|
} else {
|
||||||
Err(EvalAltResult::ErrorArrayBounds(arr.len(), index, idx_pos))
|
Err(EvalAltResult::ErrorArrayBounds(arr.len(), index, idx_pos))
|
||||||
};
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
Union::Map(map) => {
|
||||||
|
// val_map[idx]
|
||||||
|
let index = self
|
||||||
|
.eval_expr(scope, fn_lib, idx_expr, level)?
|
||||||
|
.take_string()
|
||||||
|
.map_err(|_| EvalAltResult::ErrorStringIndexExpr(idx_expr.position()))?;
|
||||||
|
|
||||||
|
return Ok((
|
||||||
|
map.get(&index)
|
||||||
|
.cloned()
|
||||||
|
.unwrap_or_else(|| Dynamic::from_unit()),
|
||||||
|
IndexValue::from_str(index),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
Union::Str(s) => {
|
||||||
|
// val_string[idx]
|
||||||
|
let index = self
|
||||||
|
.eval_expr(scope, fn_lib, idx_expr, level)?
|
||||||
|
.as_int()
|
||||||
|
.map_err(|_| EvalAltResult::ErrorNumericIndexExpr(idx_expr.position()))?;
|
||||||
|
|
||||||
|
return if index >= 0 {
|
||||||
|
s.chars()
|
||||||
|
.nth(index as usize)
|
||||||
|
.map(|ch| (Dynamic::from_char(ch), IndexValue::from_num(index)))
|
||||||
|
.ok_or_else(|| {
|
||||||
|
EvalAltResult::ErrorStringBounds(s.chars().count(), index, idx_pos)
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
Err(EvalAltResult::ErrorStringBounds(
|
||||||
|
s.chars().count(),
|
||||||
|
index,
|
||||||
|
idx_pos,
|
||||||
|
))
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Error - cannot be indexed
|
||||||
|
_ => Err(EvalAltResult::ErrorIndexingType(
|
||||||
|
type_name.to_string(),
|
||||||
|
op_pos,
|
||||||
|
)),
|
||||||
}
|
}
|
||||||
|
|
||||||
// val_map[idx]
|
|
||||||
if let Ok(map) = val.as_map() {
|
|
||||||
let index = self
|
|
||||||
.eval_expr(scope, fn_lib, idx_expr, level)?
|
|
||||||
.take_string()
|
|
||||||
.map_err(|_| EvalAltResult::ErrorStringIndexExpr(idx_expr.position()))?;
|
|
||||||
|
|
||||||
return Ok((
|
|
||||||
map.get(&index)
|
|
||||||
.cloned()
|
|
||||||
.unwrap_or_else(|| Dynamic::from_unit()),
|
|
||||||
IndexValue::from_str(index),
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
// val_string[idx]
|
|
||||||
if let Ok(s) = val.as_str() {
|
|
||||||
let index = self
|
|
||||||
.eval_expr(scope, fn_lib, idx_expr, level)?
|
|
||||||
.as_int()
|
|
||||||
.map_err(|_| EvalAltResult::ErrorNumericIndexExpr(idx_expr.position()))?;
|
|
||||||
|
|
||||||
return if index >= 0 {
|
|
||||||
s.chars()
|
|
||||||
.nth(index as usize)
|
|
||||||
.map(|ch| (Dynamic::from_char(ch), IndexValue::from_num(index)))
|
|
||||||
.ok_or_else(|| {
|
|
||||||
EvalAltResult::ErrorStringBounds(s.chars().count(), index, idx_pos)
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
Err(EvalAltResult::ErrorStringBounds(
|
|
||||||
s.chars().count(),
|
|
||||||
index,
|
|
||||||
idx_pos,
|
|
||||||
))
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// Error - cannot be indexed
|
|
||||||
Err(EvalAltResult::ErrorIndexingType(
|
|
||||||
self.map_type_name(val.type_name()).to_string(),
|
|
||||||
op_pos,
|
|
||||||
))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Evaluate an index expression
|
/// Evaluate an index expression
|
||||||
@ -815,25 +824,18 @@ impl Engine<'_> {
|
|||||||
match lhs {
|
match lhs {
|
||||||
// id[idx_expr]
|
// id[idx_expr]
|
||||||
Expr::Variable(id, _) => {
|
Expr::Variable(id, _) => {
|
||||||
let (
|
let (ScopeSource { typ, index, .. }, val) =
|
||||||
ScopeSource {
|
Self::search_scope(scope, &id, lhs.position())?;
|
||||||
typ: src_type,
|
let (val, idx) =
|
||||||
index: src_idx,
|
|
||||||
..
|
|
||||||
},
|
|
||||||
val,
|
|
||||||
) = Self::search_scope(scope, &id, lhs.position())?;
|
|
||||||
|
|
||||||
let (val, index) =
|
|
||||||
self.get_indexed_value(scope, fn_lib, &val, idx_expr, op_pos, level)?;
|
self.get_indexed_value(scope, fn_lib, &val, idx_expr, op_pos, level)?;
|
||||||
|
|
||||||
Ok((
|
Ok((
|
||||||
Some(ScopeSource {
|
Some(ScopeSource {
|
||||||
name: &id,
|
name: &id,
|
||||||
typ: src_type,
|
typ,
|
||||||
index: src_idx,
|
index,
|
||||||
}),
|
}),
|
||||||
index,
|
idx,
|
||||||
val,
|
val,
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
@ -841,7 +843,6 @@ impl Engine<'_> {
|
|||||||
// (expr)[idx_expr]
|
// (expr)[idx_expr]
|
||||||
expr => {
|
expr => {
|
||||||
let val = self.eval_expr(scope, fn_lib, expr, level)?;
|
let val = self.eval_expr(scope, fn_lib, expr, level)?;
|
||||||
|
|
||||||
self.get_indexed_value(scope, fn_lib, &val, idx_expr, op_pos, level)
|
self.get_indexed_value(scope, fn_lib, &val, idx_expr, op_pos, level)
|
||||||
.map(|(val, index)| (None, index, val))
|
.map(|(val, index)| (None, index, val))
|
||||||
}
|
}
|
||||||
@ -870,7 +871,7 @@ impl Engine<'_> {
|
|||||||
) -> Result<Dynamic, EvalAltResult> {
|
) -> Result<Dynamic, EvalAltResult> {
|
||||||
let target = scope.get_mut(src);
|
let target = scope.get_mut(src);
|
||||||
|
|
||||||
match &mut target.0 {
|
match target.get_mut() {
|
||||||
// array_id[idx] = val
|
// array_id[idx] = val
|
||||||
Union::Array(arr) => {
|
Union::Array(arr) => {
|
||||||
arr[idx.as_num()] = new_val.0;
|
arr[idx.as_num()] = new_val.0;
|
||||||
@ -903,7 +904,7 @@ impl Engine<'_> {
|
|||||||
new_val: Dynamic,
|
new_val: Dynamic,
|
||||||
pos: Position,
|
pos: Position,
|
||||||
) -> Result<Dynamic, EvalAltResult> {
|
) -> Result<Dynamic, EvalAltResult> {
|
||||||
match &mut target.0 {
|
match target.get_mut() {
|
||||||
Union::Array(arr) => {
|
Union::Array(arr) => {
|
||||||
arr[idx.as_num()] = new_val;
|
arr[idx.as_num()] = new_val;
|
||||||
}
|
}
|
||||||
@ -1083,23 +1084,23 @@ impl Engine<'_> {
|
|||||||
self.set_dot_val_helper(scope, fn_lib, this_ptr, dot_rhs, new_val, level);
|
self.set_dot_val_helper(scope, fn_lib, this_ptr, dot_rhs, new_val, level);
|
||||||
|
|
||||||
// In case the expression mutated `target`, we need to update it back into the scope because it is cloned.
|
// In case the expression mutated `target`, we need to update it back into the scope because it is cloned.
|
||||||
match src.map(|x| x.typ) {
|
if let Some(src) = src {
|
||||||
None => (),
|
match src.typ {
|
||||||
|
ScopeEntryType::Constant => {
|
||||||
|
return Err(EvalAltResult::ErrorAssignmentToConstant(
|
||||||
|
src.name.to_string(),
|
||||||
|
lhs.position(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
Some(ScopeEntryType::Constant) => {
|
ScopeEntryType::Normal => {
|
||||||
return Err(EvalAltResult::ErrorAssignmentToConstant(
|
Self::update_indexed_var_in_scope(
|
||||||
src.unwrap().name.to_string(),
|
scope,
|
||||||
lhs.position(),
|
src,
|
||||||
));
|
index,
|
||||||
}
|
(target, val_pos),
|
||||||
|
)?;
|
||||||
Some(ScopeEntryType::Normal) => {
|
}
|
||||||
Self::update_indexed_var_in_scope(
|
|
||||||
scope,
|
|
||||||
src.unwrap(),
|
|
||||||
index,
|
|
||||||
(target, val_pos),
|
|
||||||
)?;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user