Favor matching on Union's instead of downcast::<T>, as_xxx() or is::<T>.

This commit is contained in:
Stephen Chung 2020-04-16 10:24:30 +08:00
parent c799a4567a
commit 1ace4b474c
2 changed files with 156 additions and 157 deletions

View File

@ -263,8 +263,24 @@ fn cast_box<X: Variant, T: Variant>(item: Box<X>) -> Result<T, Box<X>> {
}
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.
///
/// 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
///
/// ```
@ -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 {
Self(Union::Unit(()))
}

View File

@ -94,12 +94,6 @@ enum 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 {
match self {
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)]
pub struct FnSpec<'a> {
pub name: Cow<'a, str>,
@ -497,35 +503,37 @@ impl Engine<'_> {
}
if let Some(prop) = extract_prop_from_getter(fn_name) {
return match args[0] {
// Map property access
if let Ok(map) = args[0].as_map() {
return Ok(map
Dynamic(Union::Map(map)) => Ok(map
.get(prop)
.cloned()
.unwrap_or_else(|| Dynamic::from_unit()));
}
.unwrap_or_else(|| Dynamic::from_unit())),
// Getter function not found
return Err(EvalAltResult::ErrorDotExpr(
_ => Err(EvalAltResult::ErrorDotExpr(
format!("- property '{}' unknown or write-only", prop),
pos,
));
)),
};
}
if let Some(prop) = extract_prop_from_setter(fn_name) {
let value = args[1].clone();
return match args[0] {
// Map property update
if let Dynamic(Union::Map(map)) = args[0] {
Dynamic(Union::Map(map)) => {
map.insert(prop.to_string(), value);
return Ok(Dynamic::from_unit());
Ok(Dynamic::from_unit())
}
// Setter function not found
return Err(EvalAltResult::ErrorDotExpr(
_ => Err(EvalAltResult::ErrorDotExpr(
format!("- property '{}' unknown or read-only", prop),
pos,
));
)),
};
}
if let Some(val) = def_val {
@ -610,8 +618,7 @@ impl Engine<'_> {
let mut args = [target.get_mut(scope)];
self.call_fn_raw(None, fn_lib, &make_getter(id), &mut args, None, *pos, 0)
.and_then(|mut val| {
let target = Target::from(&mut val);
self.get_dot_val_helper(scope, fn_lib, target, rhs, level)
self.get_dot_val_helper(scope, fn_lib, (&mut val).into(), rhs, level)
})
}
// 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)
.and_then(|(mut val, _)| {
let target = Target::from(&mut val);
self.get_dot_val_helper(scope, fn_lib, target, rhs, level)
self.get_dot_val_helper(scope, fn_lib, (&mut val).into(), rhs, level)
})
}
// Syntax error
@ -676,36 +682,36 @@ impl Engine<'_> {
// This is a variable property access (potential function call).
// 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].???
Expr::Index(idx_lhs, idx_expr, op_pos) => {
let (src, index, mut val) =
self.eval_index_expr(scope, fn_lib, idx_lhs, idx_expr, *op_pos, level)?;
let target = Target::from(&mut val);
let value = self.get_dot_val_helper(scope, fn_lib, target, dot_rhs, level);
let value =
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.
match src.map(|s| s.typ) {
None => (),
Some(ScopeEntryType::Constant) => {
if let Some(src) = src {
match src.typ {
ScopeEntryType::Constant => {
return Err(EvalAltResult::ErrorAssignmentToConstant(
src.unwrap().name.to_string(),
src.name.to_string(),
idx_lhs.position(),
));
}
Some(ScopeEntryType::Normal) => {
ScopeEntryType::Normal => {
Self::update_indexed_var_in_scope(
scope,
src.unwrap(),
src,
index,
(val, dot_rhs.position()),
)?;
}
}
}
value
}
@ -713,7 +719,7 @@ impl Engine<'_> {
// {expr}.???
expr => {
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,9 +746,11 @@ impl Engine<'_> {
level: usize,
) -> Result<(Dynamic, IndexValue), EvalAltResult> {
let idx_pos = idx_expr.position();
let type_name = self.map_type_name(val.type_name());
match val.get_ref() {
Union::Array(arr) => {
// val_array[idx]
if let Ok(arr) = val.as_array() {
let index = self
.eval_expr(scope, fn_lib, idx_expr, level)?
.as_int()
@ -757,8 +765,8 @@ impl Engine<'_> {
};
}
Union::Map(map) => {
// val_map[idx]
if let Ok(map) = val.as_map() {
let index = self
.eval_expr(scope, fn_lib, idx_expr, level)?
.take_string()
@ -772,8 +780,8 @@ impl Engine<'_> {
));
}
Union::Str(s) => {
// val_string[idx]
if let Ok(s) = val.as_str() {
let index = self
.eval_expr(scope, fn_lib, idx_expr, level)?
.as_int()
@ -796,10 +804,11 @@ impl Engine<'_> {
}
// Error - cannot be indexed
Err(EvalAltResult::ErrorIndexingType(
self.map_type_name(val.type_name()).to_string(),
_ => Err(EvalAltResult::ErrorIndexingType(
type_name.to_string(),
op_pos,
))
)),
}
}
/// Evaluate an index expression
@ -815,25 +824,18 @@ impl Engine<'_> {
match lhs {
// id[idx_expr]
Expr::Variable(id, _) => {
let (
ScopeSource {
typ: src_type,
index: src_idx,
..
},
val,
) = Self::search_scope(scope, &id, lhs.position())?;
let (val, index) =
let (ScopeSource { typ, index, .. }, val) =
Self::search_scope(scope, &id, lhs.position())?;
let (val, idx) =
self.get_indexed_value(scope, fn_lib, &val, idx_expr, op_pos, level)?;
Ok((
Some(ScopeSource {
name: &id,
typ: src_type,
index: src_idx,
}),
typ,
index,
}),
idx,
val,
))
}
@ -841,7 +843,6 @@ impl Engine<'_> {
// (expr)[idx_expr]
expr => {
let val = self.eval_expr(scope, fn_lib, expr, level)?;
self.get_indexed_value(scope, fn_lib, &val, idx_expr, op_pos, level)
.map(|(val, index)| (None, index, val))
}
@ -870,7 +871,7 @@ impl Engine<'_> {
) -> Result<Dynamic, EvalAltResult> {
let target = scope.get_mut(src);
match &mut target.0 {
match target.get_mut() {
// array_id[idx] = val
Union::Array(arr) => {
arr[idx.as_num()] = new_val.0;
@ -903,7 +904,7 @@ impl Engine<'_> {
new_val: Dynamic,
pos: Position,
) -> Result<Dynamic, EvalAltResult> {
match &mut target.0 {
match target.get_mut() {
Union::Array(arr) => {
arr[idx.as_num()] = new_val;
}
@ -1083,25 +1084,25 @@ impl Engine<'_> {
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.
match src.map(|x| x.typ) {
None => (),
Some(ScopeEntryType::Constant) => {
if let Some(src) = src {
match src.typ {
ScopeEntryType::Constant => {
return Err(EvalAltResult::ErrorAssignmentToConstant(
src.unwrap().name.to_string(),
src.name.to_string(),
lhs.position(),
));
}
Some(ScopeEntryType::Normal) => {
ScopeEntryType::Normal => {
Self::update_indexed_var_in_scope(
scope,
src.unwrap(),
src,
index,
(target, val_pos),
)?;
}
}
}
value
}