Merge branch 'object_maps'

This commit is contained in:
Stephen Chung
2020-03-30 16:13:12 +08:00
9 changed files with 674 additions and 221 deletions

View File

@@ -26,6 +26,10 @@ use crate::stdlib::{
#[cfg(not(feature = "no_index"))]
pub type Array = Vec<Dynamic>;
/// An dynamic hash map of `Dynamic` values.
#[cfg(not(feature = "no_object"))]
pub type Map = HashMap<String, Dynamic>;
pub type FnCallArgs<'a> = [&'a mut Variant];
pub type FnAny = dyn Fn(&mut FnCallArgs, Position) -> Result<Dynamic, EvalAltResult>;
@@ -45,6 +49,8 @@ pub(crate) const FUNC_SETTER: &str = "set$";
#[cfg(not(feature = "no_index"))]
enum IndexSourceType {
Array,
#[cfg(not(feature = "no_object"))]
Map,
String,
Expression,
}
@@ -156,6 +162,8 @@ impl Default for Engine<'_> {
let type_names = [
#[cfg(not(feature = "no_index"))]
(type_name::<Array>(), "array"),
#[cfg(not(feature = "no_object"))]
(type_name::<Map>(), "map"),
(type_name::<String>(), "string"),
(type_name::<Dynamic>(), "dynamic"),
]
@@ -325,6 +333,14 @@ impl Engine<'_> {
}
if let Some(prop) = extract_prop_from_getter(fn_name) {
#[cfg(not(feature = "no_object"))]
{
// Map property access
if let Some(map) = args[0].downcast_ref::<Map>() {
return Ok(map.get(prop).cloned().unwrap_or_else(|| ().into_dynamic()));
}
}
// Getter function not found
return Err(EvalAltResult::ErrorDotExpr(
format!("- property '{}' unknown or write-only", prop),
@@ -333,6 +349,17 @@ impl Engine<'_> {
}
if let Some(prop) = extract_prop_from_setter(fn_name) {
#[cfg(not(feature = "no_object"))]
{
let val = args[1].into_dynamic();
// Map property update
if let Some(map) = args[0].downcast_mut::<Map>() {
map.insert(prop.to_string(), val);
return Ok(().into_dynamic());
}
}
// Setter function not found
return Err(EvalAltResult::ErrorDotExpr(
format!("- property '{}' unknown or read-only", prop),
@@ -420,21 +447,17 @@ impl Engine<'_> {
// xxx.idx_lhs[idx_expr]
#[cfg(not(feature = "no_index"))]
Expr::Index(idx_lhs, idx_expr, op_pos) => {
let (val, _) = match idx_lhs.as_ref() {
let val = match idx_lhs.as_ref() {
// xxx.id[idx_expr]
Expr::Property(id, pos) => {
let get_fn_name = make_getter(id);
let this_ptr = get_this_ptr(scope, src, target);
(
self.call_fn_raw(&get_fn_name, &mut [this_ptr], None, *pos, 0)?,
*pos,
)
self.call_fn_raw(&get_fn_name, &mut [this_ptr], None, *pos, 0)?
}
// xxx.???[???][idx_expr]
Expr::Index(_, _, _) => (
self.get_dot_val_helper(scope, src, target, idx_lhs, level)?,
*op_pos,
),
Expr::Index(_, _, _) => {
self.get_dot_val_helper(scope, src, target, idx_lhs, level)?
}
// Syntax error
_ => {
return Err(EvalAltResult::ErrorDotExpr(
@@ -444,9 +467,8 @@ impl Engine<'_> {
}
};
let idx = self.eval_index_value(scope, idx_expr, level)?;
self.get_indexed_value(&val, idx, idx_expr.position(), *op_pos)
.map(|(v, _)| v)
self.get_indexed_value(scope, &val, idx_expr, *op_pos, level)
.map(|(v, _, _)| v)
}
// xxx.dot_lhs.rhs
@@ -464,21 +486,17 @@ impl Engine<'_> {
// xxx.idx_lhs[idx_expr].rhs
#[cfg(not(feature = "no_index"))]
Expr::Index(idx_lhs, idx_expr, op_pos) => {
let (val, _) = match idx_lhs.as_ref() {
let val = match idx_lhs.as_ref() {
// xxx.id[idx_expr].rhs
Expr::Property(id, pos) => {
let get_fn_name = make_getter(id);
let this_ptr = get_this_ptr(scope, src, target);
(
self.call_fn_raw(&get_fn_name, &mut [this_ptr], None, *pos, 0)?,
*pos,
)
self.call_fn_raw(&get_fn_name, &mut [this_ptr], None, *pos, 0)?
}
// xxx.???[???][idx_expr].rhs
Expr::Index(_, _, _) => (
self.get_dot_val_helper(scope, src, target, idx_lhs, level)?,
*op_pos,
),
Expr::Index(_, _, _) => {
self.get_dot_val_helper(scope, src, target, idx_lhs, level)?
}
// Syntax error
_ => {
return Err(EvalAltResult::ErrorDotExpr(
@@ -488,9 +506,8 @@ impl Engine<'_> {
}
};
let idx = self.eval_index_value(scope, idx_expr, level)?;
self.get_indexed_value(&val, idx, idx_expr.position(), *op_pos)
.and_then(|(mut v, _)| {
self.get_indexed_value(scope, &val, idx_expr, *op_pos, level)
.and_then(|(mut v, _, _)| {
self.get_dot_val_helper(scope, None, Some(v.as_mut()), rhs, level)
})
}
@@ -521,7 +538,7 @@ impl Engine<'_> {
match dot_lhs {
// id.???
Expr::Variable(id, pos) => {
let (entry, _) = Self::search_scope(scope, id, Ok, *pos)?;
let (entry, _) = Self::search_scope(scope, id, *pos)?;
// Avoid referencing scope which is used below as mut
let entry = ScopeSource { name: id, ..entry };
@@ -534,7 +551,7 @@ impl Engine<'_> {
// idx_lhs[idx_expr].???
#[cfg(not(feature = "no_index"))]
Expr::Index(idx_lhs, idx_expr, op_pos) => {
let (src_type, src, idx, mut target) =
let (idx_src_type, src, idx, mut target) =
self.eval_index_expr(scope, idx_lhs, idx_expr, *op_pos, level)?;
let this_ptr = target.as_mut();
let val = self.get_dot_val_helper(scope, None, Some(this_ptr), dot_rhs, level);
@@ -550,7 +567,7 @@ impl Engine<'_> {
}
ScopeEntryType::Normal => {
Self::update_indexed_var_in_scope(
src_type,
idx_src_type,
scope,
src,
idx,
@@ -573,61 +590,85 @@ impl Engine<'_> {
}
/// Search for a variable within the scope, returning its value and index inside the Scope
fn search_scope<'a, T>(
fn search_scope<'a>(
scope: &'a Scope,
id: &str,
convert: impl FnOnce(Dynamic) -> Result<T, EvalAltResult>,
begin: Position,
) -> Result<(ScopeSource<'a>, T), EvalAltResult> {
) -> Result<(ScopeSource<'a>, Dynamic), EvalAltResult> {
scope
.get(id)
.ok_or_else(|| EvalAltResult::ErrorVariableNotFound(id.into(), begin))
.and_then(move |(src, value)| convert(value).map(|v| (src, v)))
}
/// Evaluate the value of an index (must evaluate to INT)
#[cfg(not(feature = "no_index"))]
fn eval_index_value(
&mut self,
scope: &mut Scope,
idx_expr: &Expr,
level: usize,
) -> Result<INT, EvalAltResult> {
self.eval_expr(scope, idx_expr, level)?
.downcast::<INT>()
.map(|v| *v)
.map_err(|_| EvalAltResult::ErrorIndexExpr(idx_expr.position()))
}
/// Get the value at the indexed position of a base type
#[cfg(not(feature = "no_index"))]
fn get_indexed_value(
&self,
&mut self,
scope: &mut Scope,
val: &Dynamic,
idx: INT,
idx_pos: Position,
idx_expr: &Expr,
op_pos: Position,
) -> Result<(Dynamic, IndexSourceType), EvalAltResult> {
level: usize,
) -> Result<(Dynamic, IndexSourceType, (Option<usize>, Option<String>)), EvalAltResult> {
let idx_pos = idx_expr.position();
if val.is::<Array>() {
// val_array[idx]
let arr = val.downcast_ref::<Array>().expect("array expected");
if idx >= 0 {
let idx = *self
.eval_expr(scope, idx_expr, level)?
.downcast::<INT>()
.map_err(|_| EvalAltResult::ErrorNumericIndexExpr(idx_expr.position()))?;
return if idx >= 0 {
arr.get(idx as usize)
.cloned()
.map(|v| (v, IndexSourceType::Array))
.map(|v| (v, IndexSourceType::Array, (Some(idx as usize), None)))
.ok_or_else(|| EvalAltResult::ErrorArrayBounds(arr.len(), idx, idx_pos))
} else {
Err(EvalAltResult::ErrorArrayBounds(arr.len(), idx, idx_pos))
};
}
#[cfg(not(feature = "no_object"))]
{
if val.is::<Map>() {
// val_map[idx]
let map = val.downcast_ref::<Map>().expect("array expected");
let idx = *self
.eval_expr(scope, idx_expr, level)?
.downcast::<String>()
.map_err(|_| EvalAltResult::ErrorStringIndexExpr(idx_expr.position()))?;
return Ok((
map.get(&idx).cloned().unwrap_or_else(|| ().into_dynamic()),
IndexSourceType::Map,
(None, Some(idx)),
));
}
} else if val.is::<String>() {
}
if val.is::<String>() {
// val_string[idx]
let s = val.downcast_ref::<String>().expect("string expected");
if idx >= 0 {
let idx = *self
.eval_expr(scope, idx_expr, level)?
.downcast::<INT>()
.map_err(|_| EvalAltResult::ErrorNumericIndexExpr(idx_expr.position()))?;
return if idx >= 0 {
s.chars()
.nth(idx as usize)
.map(|ch| (ch.into_dynamic(), IndexSourceType::String))
.map(|ch| {
(
ch.into_dynamic(),
IndexSourceType::String,
(Some(idx as usize), None),
)
})
.ok_or_else(|| {
EvalAltResult::ErrorStringBounds(s.chars().count(), idx, idx_pos)
})
@@ -637,14 +678,14 @@ impl Engine<'_> {
idx,
idx_pos,
))
}
} else {
// Error - cannot be indexed
Err(EvalAltResult::ErrorIndexingType(
self.map_type_name(val.type_name()).to_string(),
op_pos,
))
};
}
// Error - cannot be indexed
return Err(EvalAltResult::ErrorIndexingType(
self.map_type_name(val.type_name()).to_string(),
op_pos,
));
}
/// Evaluate an index expression
@@ -656,36 +697,48 @@ impl Engine<'_> {
idx_expr: &Expr,
op_pos: Position,
level: usize,
) -> Result<(IndexSourceType, Option<ScopeSource<'a>>, usize, Dynamic), EvalAltResult> {
let idx = self.eval_index_value(scope, idx_expr, level)?;
) -> Result<
(
IndexSourceType,
Option<ScopeSource<'a>>,
(Option<usize>, Option<String>),
Dynamic,
),
EvalAltResult,
> {
match lhs {
// id[idx_expr]
Expr::Variable(id, _) => Self::search_scope(
scope,
&id,
|val| self.get_indexed_value(&val, idx, idx_expr.position(), op_pos),
lhs.position(),
)
.map(|(src, (val, src_type))| {
(
src_type,
Expr::Variable(id, _) => {
let (
ScopeSource {
typ: src_type,
index: src_idx,
..
},
val,
) = Self::search_scope(scope, &id, lhs.position())?;
let (val, idx_src_type, idx) =
self.get_indexed_value(scope, &val, idx_expr, op_pos, level)?;
Ok((
idx_src_type,
Some(ScopeSource {
name: &id,
typ: src.typ,
index: src.index,
typ: src_type,
index: src_idx,
}),
idx as usize,
idx,
val,
)
}),
))
}
// (expr)[idx_expr]
expr => {
let val = self.eval_expr(scope, expr, level)?;
self.get_indexed_value(&val, idx, idx_expr.position(), op_pos)
.map(|(v, _)| (IndexSourceType::Expression, None, idx as usize, v))
self.get_indexed_value(scope, &val, idx_expr, op_pos, level)
.map(|(v, _, idx)| (IndexSourceType::Expression, None, idx, v))
}
}
}
@@ -707,17 +760,25 @@ impl Engine<'_> {
/// Update the value at an index position in a variable inside the scope
#[cfg(not(feature = "no_index"))]
fn update_indexed_var_in_scope(
src_type: IndexSourceType,
idx_src_type: IndexSourceType,
scope: &mut Scope,
src: ScopeSource,
idx: usize,
idx: (Option<usize>, Option<String>),
new_val: (Dynamic, Position),
) -> Result<Dynamic, EvalAltResult> {
match src_type {
match idx_src_type {
// array_id[idx] = val
IndexSourceType::Array => {
let arr = scope.get_mut_by_type::<Array>(src);
arr[idx as usize] = new_val.0;
arr[idx.0.expect("should be Some")] = new_val.0;
Ok(().into_dynamic())
}
// map_id[idx] = val
#[cfg(not(feature = "no_object"))]
IndexSourceType::Map => {
let arr = scope.get_mut_by_type::<Map>(src);
arr.insert(idx.1.expect("should be Some"), new_val.0);
Ok(().into_dynamic())
}
@@ -730,7 +791,7 @@ impl Engine<'_> {
.0
.downcast::<char>()
.map_err(|_| EvalAltResult::ErrorCharMismatch(pos))?;
Self::str_replace_char(s, idx as usize, ch);
Self::str_replace_char(s, idx.0.expect("should be Some"), ch);
Ok(().into_dynamic())
}
@@ -742,26 +803,37 @@ impl Engine<'_> {
#[cfg(not(feature = "no_index"))]
fn update_indexed_value(
mut target: Dynamic,
idx: usize,
idx: (Option<usize>, Option<String>),
new_val: Dynamic,
pos: Position,
) -> Result<Dynamic, EvalAltResult> {
if target.is::<Array>() {
let arr = target.downcast_mut::<Array>().expect("array expected");
arr[idx as usize] = new_val;
} else if target.is::<String>() {
arr[idx.0.expect("should be Some")] = new_val;
return Ok(target);
}
#[cfg(not(feature = "no_object"))]
{
if target.is::<Map>() {
let map = target.downcast_mut::<Map>().expect("array expected");
map.insert(idx.1.expect("should be Some"), new_val);
return Ok(target);
}
}
if target.is::<String>() {
let s = target.downcast_mut::<String>().expect("string expected");
// Value must be a character
let ch = *new_val
.downcast::<char>()
.map_err(|_| EvalAltResult::ErrorCharMismatch(pos))?;
Self::str_replace_char(s, idx as usize, ch);
} else {
// All other variable types should be an error
panic!("array or string source type expected for indexing")
Self::str_replace_char(s, idx.0.expect("should be Some"), ch);
return Ok(target);
}
Ok(target)
// All other variable types should be an error
panic!("array, map or string source type expected for indexing")
}
/// Chain-evaluate a dot setter
@@ -792,13 +864,10 @@ impl Engine<'_> {
self.call_fn_raw(&get_fn_name, &mut [this_ptr], None, *pos, 0)
.and_then(|v| {
let idx = self.eval_index_value(scope, idx_expr, level)?;
Self::update_indexed_value(
v,
idx as usize,
new_val.0.clone(),
new_val.1,
)
let (_, _, idx) =
self.get_indexed_value(scope, &v, idx_expr, *op_pos, level)?;
Self::update_indexed_value(v, idx, new_val.0.clone(), new_val.1)
})
.and_then(|mut v| {
let set_fn_name = make_setter(id);
@@ -842,16 +911,15 @@ impl Engine<'_> {
self.call_fn_raw(&get_fn_name, &mut [this_ptr], None, *pos, 0)
.and_then(|v| {
let idx = self.eval_index_value(scope, idx_expr, level)?;
let (mut target, _) =
self.get_indexed_value(&v, idx, idx_expr.position(), *op_pos)?;
let (mut target, _, idx) =
self.get_indexed_value(scope, &v, idx_expr, *op_pos, level)?;
let val_pos = new_val.1;
let this_ptr = target.as_mut();
self.set_dot_val_helper(scope, this_ptr, rhs, new_val, level)?;
// In case the expression mutated `target`, we need to update it back into the scope because it is cloned.
Self::update_indexed_value(v, idx as usize, target, val_pos)
Self::update_indexed_value(v, idx, target, val_pos)
})
.and_then(|mut v| {
let set_fn_name = make_setter(id);
@@ -896,7 +964,7 @@ impl Engine<'_> {
match dot_lhs {
// id.???
Expr::Variable(id, pos) => {
let (entry, mut target) = Self::search_scope(scope, id, Ok, *pos)?;
let (entry, mut target) = Self::search_scope(scope, id, *pos)?;
match entry.typ {
ScopeEntryType::Constant => Err(EvalAltResult::ErrorAssignmentToConstant(
@@ -921,7 +989,7 @@ impl Engine<'_> {
// TODO - Allow chaining of indexing!
#[cfg(not(feature = "no_index"))]
Expr::Index(lhs, idx_expr, op_pos) => {
let (src_type, src, idx, mut target) =
let (idx_src_type, src, idx, mut target) =
self.eval_index_expr(scope, lhs, idx_expr, *op_pos, level)?;
let val_pos = new_val.1;
let this_ptr = target.as_mut();
@@ -938,7 +1006,7 @@ impl Engine<'_> {
}
ScopeEntryType::Normal => {
Self::update_indexed_var_in_scope(
src_type,
idx_src_type,
scope,
src,
idx,
@@ -973,7 +1041,7 @@ impl Engine<'_> {
Expr::IntegerConstant(i, _) => Ok(i.into_dynamic()),
Expr::StringConstant(s, _) => Ok(s.into_dynamic()),
Expr::CharConstant(c, _) => Ok(c.into_dynamic()),
Expr::Variable(id, pos) => Self::search_scope(scope, id, Ok, *pos).map(|(_, val)| val),
Expr::Variable(id, pos) => Self::search_scope(scope, id, *pos).map(|(_, val)| val),
Expr::Property(_, _) => panic!("unexpected property."),
// lhs[idx_expr]
@@ -1020,7 +1088,7 @@ impl Engine<'_> {
// idx_lhs[idx_expr] = rhs
#[cfg(not(feature = "no_index"))]
Expr::Index(idx_lhs, idx_expr, op_pos) => {
let (src_type, src, idx, _) =
let (idx_src_type, src, idx, _) =
self.eval_index_expr(scope, idx_lhs, idx_expr, *op_pos, level)?;
if let Some(src) = src {
@@ -1032,7 +1100,7 @@ impl Engine<'_> {
))
}
ScopeEntryType::Normal => Ok(Self::update_indexed_var_in_scope(
src_type,
idx_src_type,
scope,
src,
idx,
@@ -1073,7 +1141,7 @@ impl Engine<'_> {
#[cfg(not(feature = "no_index"))]
Expr::Array(contents, _) => {
let mut arr = Vec::new();
let mut arr = Array::new();
contents.into_iter().try_for_each(|item| {
self.eval_expr(scope, item, level).map(|val| arr.push(val))
@@ -1082,6 +1150,19 @@ impl Engine<'_> {
Ok(Box::new(arr))
}
#[cfg(not(feature = "no_object"))]
Expr::Map(contents, _) => {
let mut map = Map::new();
contents.into_iter().try_for_each(|item| {
self.eval_expr(scope, &item.1, level).map(|val| {
map.insert(item.0.clone(), val);
})
})?;
Ok(Box::new(map))
}
Expr::FunctionCall(fn_name, args_expr_list, def_val, pos) => {
// Has a system function an override?
fn has_override(engine: &Engine, name: &str) -> bool {