Skip to content

Commit

Permalink
Merge pull request #846 from schungx/master
Browse files Browse the repository at this point in the history
Minor fixups for string slice selector.
  • Loading branch information
schungx committed Mar 19, 2024
2 parents 6dc86bb + ab97a3a commit 10951b7
Show file tree
Hide file tree
Showing 10 changed files with 118 additions and 109 deletions.
5 changes: 3 additions & 2 deletions CHANGELOG.md
Expand Up @@ -19,9 +19,10 @@ Deprecated API's
New features
------------

* Sub-strings can now be selected from full strings by indexing via ranges (e.g. `s[1..4]`).
* New options `Engine::set_max_strings_interned` and `Engine::max_strings_interned` are added to limit the maximum number of strings interned in the `Engine`'s string interner.
* A new advanced callback can now be registered (gated under the `internals` feature), `Engine::on_invalid_array_index`, to handle access to missing properties in object maps.
* A new advanced callback can now be registered (gated under the `internals` feature), `Engine::on_missing_map_property`, to handle out-of-bound index into arrays.
* A new advanced callback, `Engine::on_invalid_array_index`, is added (gated under the `internals` feature) to handle access to missing properties in object maps.
* A new advanced callback, `Engine::on_missing_map_property`, is added (gated under the `internals` feature) to handle out-of-bound index into arrays.

Enhancements
------------
Expand Down
Binary file not shown.
1 change: 0 additions & 1 deletion fuzz/clusterfuzz-testcase-scripting-4756460147900416

This file was deleted.

4 changes: 1 addition & 3 deletions src/api/run.rs
Expand Up @@ -128,9 +128,7 @@ impl Engine {
global.lib.push(ast.shared_lib().clone());

#[cfg(not(feature = "no_module"))]
{
global.embedded_module_resolver = ast.resolver.clone();
}
global.embedded_module_resolver.clone_from(&ast.resolver);

let _ = self.eval_global_statements(global, caches, scope, ast.statements(), true)?;

Expand Down
6 changes: 3 additions & 3 deletions src/ast/ast.rs
Expand Up @@ -479,8 +479,8 @@ impl AST {
other.resolver.as_deref().map_or(true, |r| r.is_empty()),
) {
(true, true) => (),
(false, true) => _ast.resolver = self.resolver.clone(),
(true, false) => _ast.resolver = other.resolver.clone(),
(false, true) => _ast.resolver.clone_from(&self.resolver),
(true, false) => _ast.resolver.clone_from(&other.resolver),
(false, false) => {
let mut resolver = self.resolver.as_deref().unwrap().clone();
for (k, v) in other.resolver.as_deref().unwrap() {
Expand Down Expand Up @@ -573,7 +573,7 @@ impl AST {
other.resolver.as_deref().map_or(true, |r| r.is_empty()),
) {
(_, true) => (),
(true, false) => self.resolver = other.resolver.clone(),
(true, false) => self.resolver.clone_from(&other.resolver),
(false, false) => {
let resolver = crate::func::shared_make_mut(self.resolver.as_mut().unwrap());
let other_resolver = crate::func::shared_take_or_clone(other.resolver.unwrap());
Expand Down
6 changes: 6 additions & 0 deletions src/ast/expr.rs
Expand Up @@ -533,6 +533,9 @@ impl Expr {
(Self::IntegerConstant(ref start, ..), Self::Unit(..)) => {
(*start..INT::MAX).into()
}
(Self::Unit(..), Self::IntegerConstant(ref start, ..)) => {
(0..*start).into()
}
_ => return None,
},
// x..=y
Expand All @@ -544,6 +547,9 @@ impl Expr {
(Self::IntegerConstant(ref start, ..), Self::Unit(..)) => {
(*start..=INT::MAX).into()
}
(Self::Unit(..), Self::IntegerConstant(ref start, ..)) => {
(0..=*start).into()
}
_ => return None,
},
_ => return None,
Expand Down
135 changes: 65 additions & 70 deletions src/eval/chaining.rs
Expand Up @@ -4,11 +4,10 @@
use super::{Caches, GlobalRuntimeState, Target};
use crate::ast::{ASTFlags, BinaryExpr, Expr, OpAssignment};
use crate::engine::{FN_IDX_GET, FN_IDX_SET};
use crate::tokenizer::Token;
use crate::types::dynamic::Union;
use crate::{
calc_fn_hash, Dynamic, Engine, FnArgsVec, OnceCell, Position, RhaiResult, RhaiResultOf, Scope,
ERR,
calc_fn_hash, Dynamic, Engine, ExclusiveRange, FnArgsVec, InclusiveRange, OnceCell, Position,

Check warning on line 9 in src/eval/chaining.rs

View workflow job for this annotation

GitHub Actions / Build (ubuntu-latest, --features testing-environ,no_index,serde,metadata,internals,debugging, sta...

unused imports: `ExclusiveRange`, `InclusiveRange`
RhaiResult, RhaiResultOf, Scope, ERR,
};
use std::hash::Hash;
#[cfg(feature = "no_std")]
Expand Down Expand Up @@ -347,75 +346,71 @@ impl Engine {
index: offset,
})
}
Err(typ) => {
if typ == "core::ops::range::Range<i64>" {
// val_str[range]
let idx = idx.read_lock::<core::ops::Range<i64>>().unwrap();
let range = &*idx;
let chars_count = s.chars().count();
let start = if range.start >= 0 {
range.start as usize
} else {
super::calc_index(chars_count, range.start, true, || {
ERR::ErrorStringBounds(chars_count, range.start, idx_pos).into()
})?
};
let end = if range.end >= 0 {
range.end as usize
} else {
super::calc_index(chars_count, range.end, true, || {
ERR::ErrorStringBounds(chars_count, range.end, idx_pos).into()
})
.unwrap_or(0)
};

let take = if end > start { end - start } else { 0 };

let value = s.chars().skip(start).take(take).collect::<String>();
return Ok(Target::StringSlice {
source: target,
value: value.into(),
start: start,
end: end,
exclusive: true,
});
} else if typ == "core::ops::range::RangeInclusive<i64>" {
// val_str[range]
let idx = idx.read_lock::<core::ops::RangeInclusive<i64>>().unwrap();
let range = &*idx;
let chars_count = s.chars().count();
let start = if *range.start() >= 0 {
*range.start() as usize
} else {
super::calc_index(chars_count, *range.start(), true, || {
ERR::ErrorStringBounds(chars_count, *range.start(), idx_pos)
.into()
})?
};
let end = if *range.end() >= 0 {
*range.end() as usize
} else {
super::calc_index(chars_count, *range.end(), true, || {
ERR::ErrorStringBounds(chars_count, *range.end(), idx_pos)
.into()
})
.unwrap_or(0)
};
Err(typ) if typ == std::any::type_name::<ExclusiveRange>() => {
// val_str[range]
let idx = idx.read_lock::<ExclusiveRange>().unwrap();
let range = &*idx;
let chars_count = s.chars().count();
let start = if range.start >= 0 {
range.start as usize
} else {
super::calc_index(chars_count, range.start, true, || {
ERR::ErrorStringBounds(chars_count, range.start, idx_pos).into()
})?
};
let end = if range.end >= 0 {
range.end as usize
} else {
super::calc_index(chars_count, range.end, true, || {
ERR::ErrorStringBounds(chars_count, range.end, idx_pos).into()
})
.unwrap_or(0)
};

let take = if end > start { end - start + 1 } else { 0 };
let take = if end > start { end - start } else { 0 };
let value = s.chars().skip(start).take(take).collect::<String>();

let value = s.chars().skip(start).take(take).collect::<String>();
return Ok(Target::StringSlice {
source: target,
value: value.into(),
start,
end,
exclusive: false,
});
Ok(Target::StringSlice {
source: target,
value: value.into(),
start: start,
end: end,
exclusive: true,
})
}
Err(typ) if typ == std::any::type_name::<InclusiveRange>() => {
// val_str[range]
let idx = idx.read_lock::<InclusiveRange>().unwrap();
let range = &*idx;
let chars_count = s.chars().count();
let start = if *range.start() >= 0 {
*range.start() as usize
} else {
return Err(self.make_type_mismatch_err::<crate::INT>(typ, idx_pos));
}
super::calc_index(chars_count, *range.start(), true, || {
ERR::ErrorStringBounds(chars_count, *range.start(), idx_pos).into()
})?
};
let end = if *range.end() >= 0 {
*range.end() as usize
} else {
super::calc_index(chars_count, *range.end(), true, || {
ERR::ErrorStringBounds(chars_count, *range.end(), idx_pos).into()
})
.unwrap_or(0)
};

let take = if end > start { end - start + 1 } else { 0 };
let value = s.chars().skip(start).take(take).collect::<String>();

Ok(Target::StringSlice {
source: target,
value: value.into(),
start,
end,
exclusive: false,
})
}
Err(typ) => Err(self.make_type_mismatch_err::<crate::INT>(typ, idx_pos)),
}
}

Expand Down Expand Up @@ -476,8 +471,8 @@ impl Engine {
}
#[cfg(not(feature = "no_index"))]
(Expr::FnCall(fnc, _), ChainType::Indexing)
if fnc.op_token == Some(Token::InclusiveRange)
|| fnc.op_token == Some(Token::ExclusiveRange) =>
if fnc.op_token == Some(crate::tokenizer::Token::InclusiveRange)
|| fnc.op_token == Some(crate::tokenizer::Token::ExclusiveRange) =>
{
idx_values.push(rhs.get_literal_value().unwrap())
}
Expand Down
45 changes: 16 additions & 29 deletions src/eval/target.rs
Expand Up @@ -189,14 +189,13 @@ impl<'a> Target<'a> {
Self::RefMut(..) => true,
#[cfg(not(feature = "no_closure"))]
Self::SharedValue { .. } => true,
#[cfg(not(feature = "no_index"))]
Self::StringSlice { .. } => true,
Self::TempValue(..) => false,
#[cfg(not(feature = "no_index"))]
Self::Bit { .. }
| Self::BitField { .. }
| Self::BlobByte { .. }
| Self::StringChar { .. } => false,
| Self::StringChar { .. }
| Self::StringSlice { .. } => false,
}
}
/// Is the [`Target`] a temp value?
Expand All @@ -207,14 +206,13 @@ impl<'a> Target<'a> {
Self::RefMut(..) => false,
#[cfg(not(feature = "no_closure"))]
Self::SharedValue { .. } => false,
#[cfg(not(feature = "no_index"))]
Self::StringSlice { .. } => true,
Self::TempValue(..) => true,
#[cfg(not(feature = "no_index"))]
Self::Bit { .. }
| Self::BitField { .. }
| Self::BlobByte { .. }
| Self::StringChar { .. } => false,
| Self::StringChar { .. }
| Self::StringSlice { .. } => false,
}
}
/// Is the [`Target`] a shared value?
Expand All @@ -225,13 +223,13 @@ impl<'a> Target<'a> {
return match self {
Self::RefMut(r) => r.is_shared(),
Self::SharedValue { .. } => true,
Self::StringSlice { .. } => true,
Self::TempValue(value) => value.is_shared(),
#[cfg(not(feature = "no_index"))]
Self::Bit { .. }
| Self::BitField { .. }
| Self::BlobByte { .. }
| Self::StringChar { .. } => false,
| Self::StringChar { .. }
| Self::StringSlice { .. } => false,
};
#[cfg(feature = "no_closure")]
return false;
Expand All @@ -245,18 +243,11 @@ impl<'a> Target<'a> {
Self::SharedValue { shared_value, .. } => shared_value, // Original shared value is simply taken
Self::TempValue(value) => value, // Owned value is simply taken
#[cfg(not(feature = "no_index"))]
Self::Bit { value, .. } => value, // boolean is taken
#[cfg(not(feature = "no_index"))]
Self::BitField { value, .. } => value, // INT is taken
#[cfg(not(feature = "no_index"))]
Self::BlobByte { value, .. } => value, // byte is taken
#[cfg(not(feature = "no_index"))]
Self::StringChar { value, .. } => value, // char is taken
#[cfg(not(feature = "no_index"))]
Self::StringSlice { value, .. } => {
// Slice of a string is cloned
Dynamic::from(value.to_string())
}
Self::Bit { value, .. }
| Self::BitField { value, .. }
| Self::BlobByte { value, .. }
| Self::StringChar { value, .. }
| Self::StringSlice { value, .. } => value, // Intermediate value is simply taken
}
}
/// Take a `&mut Dynamic` reference from the `Target`.
Expand Down Expand Up @@ -288,15 +279,11 @@ impl<'a> Target<'a> {
Self::SharedValue { guard, .. } => guard,
Self::TempValue(value) => value,
#[cfg(not(feature = "no_index"))]
Self::Bit { source, .. } => source,
#[cfg(not(feature = "no_index"))]
Self::BitField { source, .. } => source,
#[cfg(not(feature = "no_index"))]
Self::BlobByte { source, .. } => source,
#[cfg(not(feature = "no_index"))]
Self::StringChar { source, .. } => source,
#[cfg(not(feature = "no_index"))]
Self::StringSlice { source, .. } => source,
Self::Bit { source, .. }
| Self::BitField { source, .. }
| Self::BlobByte { source, .. }
| Self::StringChar { source, .. }
| Self::StringSlice { source, .. } => source,
}
}
/// Propagate a changed value back to the original source.
Expand Down
23 changes: 23 additions & 0 deletions src/func/builtin.rs
Expand Up @@ -448,6 +448,29 @@ pub fn get_builtin_binary_op_fn(op: &Token, x: &Dynamic, y: &Dynamic) -> Option<
impl_decimal!(INT, as_int, Decimal, as_decimal);
}

// Ranges
if *op == ExclusiveRange && type1 == TypeId::of::<INT>() && type2 == TypeId::of::<()>() {
return Some((
|_ctx, args| Ok((args[0].as_int().unwrap()..INT::MAX).into()),
false,
));
} else if *op == ExclusiveRange && type1 == TypeId::of::<()>() && type2 == TypeId::of::<INT>() {
return Some((
|_ctx, args| Ok((0..args[1].as_int().unwrap()).into()),
false,
));
} else if *op == ExclusiveRange && type1 == TypeId::of::<INT>() && type2 == TypeId::of::<()>() {
return Some((
|_ctx, args| Ok((args[0].as_int().unwrap()..=INT::MAX).into()),
false,
));
} else if *op == ExclusiveRange && type1 == TypeId::of::<()>() && type2 == TypeId::of::<INT>() {
return Some((
|_ctx, args| Ok((0..=args[1].as_int().unwrap()).into()),
false,
));
}

// char op string
if (type1, type2) == (TypeId::of::<char>(), TypeId::of::<ImmutableString>()) {
fn get_s1s2(args: &FnCallArgs) -> ([Option<char>; 2], [Option<char>; 2]) {
Expand Down
2 changes: 1 addition & 1 deletion src/module/mod.rs
Expand Up @@ -2029,7 +2029,7 @@ impl Module {
})
.map(|(&k, f)| (k, f.clone())),
),
None => self.functions = other.functions.clone(),
None => self.functions.clone_from(&other.functions),
}
}
self.dynamic_functions_filter += &other.dynamic_functions_filter;
Expand Down

0 comments on commit 10951b7

Please sign in to comment.