diff --git a/README.md b/README.md
index e4d6d354..14b205cf 100644
--- a/README.md
+++ b/README.md
@@ -1134,18 +1134,18 @@ record == "Bob X. Davis: age 42 ❤\n";
### Built-in functions
-The following standard functions (defined in the standard library but excluded if using a [raw `Engine`]) operate on strings:
+The following standard methods (defined in the standard library but excluded if using a [raw `Engine`]) operate on strings:
-| Function | Description |
-| ---------- | ------------------------------------------------------------------------ |
-| `len` | returns the number of characters (not number of bytes) in the string |
-| `pad` | pads the string with an character until a specified number of characters |
-| `append` | Adds a character or a string to the end of another string |
-| `clear` | empties the string |
-| `truncate` | cuts off the string at exactly a specified number of characters |
-| `contains` | checks if a certain character or sub-string occurs in the string |
-| `replace` | replaces a substring with another |
-| `trim` | trims the string |
+| Function | Parameter(s) | Description |
+| ---------- | ------------------------------------- | -------------------------------------------------------------------- |
+| `len` | _none_ | returns the number of characters (not number of bytes) in the string |
+| `pad` | character to pad, target length | pads the string with an character to a specified length |
+| `append` | character/string to append | Adds a character or a string to the end of another string |
+| `clear` | _none_ | empties the string |
+| `truncate` | target length | cuts off the string at exactly a specified number of characters |
+| `contains` | character/sub-string to search for | checks if a certain character or sub-string occurs in the string |
+| `replace` | target sub-string, replacement string | replaces a substring with another |
+| `trim` | _none_ | trims the string of whitespace at the beginning and end |
### Examples
@@ -1193,30 +1193,49 @@ Arrays are disabled via the [`no_index`] feature.
### Built-in functions
-The following functions (defined in the standard library but excluded if using a [raw `Engine`]) operate on arrays:
+The following methods (defined in the standard library but excluded if using a [raw `Engine`]) operate on arrays:
-| Function | Description |
-| ------------ | ------------------------------------------------------------------------------------- |
-| `push` | inserts an element at the end |
-| `append` | concatenates the second array to the end of the first |
-| `+` operator | concatenates the first array with the second |
-| `pop` | removes the last element and returns it ([`()`] if empty) |
-| `shift` | removes the first element and returns it ([`()`] if empty) |
-| `len` | returns the number of elements |
-| `pad` | pads the array with an element until a specified length |
-| `clear` | empties the array |
-| `truncate` | cuts off the array at exactly a specified length (discarding all subsequent elements) |
+| Function | Parameter(s) | Description |
+| ------------ | --------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------- |
+| `push` | element to insert | inserts an element at the end |
+| `append` | array to append | concatenates the second array to the end of the first |
+| `+` operator | first array, second array | concatenates the first array with the second |
+| `insert` | element to insert, position
(beginning if <= 0, end if >= length) | insert an element at a certain index |
+| `pop` | _none_ | removes the last element and returns it ([`()`] if empty) |
+| `shift` | _none_ | removes the first element and returns it ([`()`] if empty) |
+| `remove` | index | removes an element at a particular index and returns it, or returns [`()`] if the index is not valid |
+| `len` | _none_ | returns the number of elements |
+| `pad` | element to pad, target length | pads the array with an element until a specified length |
+| `clear` | _none_ | empties the array |
+| `truncate` | target length | cuts off the array at exactly a specified length (discarding all subsequent elements) |
### Examples
```rust
-let y = [1, 2, 3]; // array literal with 3 elements
-y[1] = 42;
+let y = [2, 3]; // array literal with 2 elements
-print(1 in y); // use 'in' to test if an item exists in the array, prints true
-print(9 in y); // ... prints false
+y.insert(0, 1); // insert element at the beginning
+y.insert(999, 4); // insert element at the end
-print(y[1]); // prints 42
+y.len() == 4;
+
+y[0] == 1;
+y[1] == 2;
+y[2] == 3;
+y[3] == 4;
+
+(1 in y) == true; // use 'in' to test if an item exists in the array
+(42 in y) == false;
+
+y[1] = 42; // array elements can be reassigned
+
+(42 in y) == true;
+
+y.remove(2) == 3; // remove element
+
+y.len() == 3;
+
+y[2] == 4; // elements after the removed element are shifted
ts.list = y; // arrays can be assigned completely (by value copy)
let foo = ts.list[1];
@@ -1238,7 +1257,7 @@ foo == 1;
y.push(4); // 4 elements
y.push(5); // 5 elements
-print(y.len()); // prints 5
+y.len() == 5;
let first = y.shift(); // remove the first element, 4 elements remaining
first == 1;
@@ -1246,7 +1265,7 @@ first == 1;
let last = y.pop(); // remove the last element, 3 elements remaining
last == 5;
-print(y.len()); // prints 3
+y.len() == 3;
for item in y { // arrays can be iterated with a 'for' statement
print(item);
@@ -1254,15 +1273,15 @@ for item in y { // arrays can be iterated with a 'for' statement
y.pad(10, "hello"); // pad the array up to 10 elements
-print(y.len()); // prints 10
+y.len() == 10;
y.truncate(5); // truncate the array to 5 elements
-print(y.len()); // prints 5
+y.len() == 5;
y.clear(); // empty the array
-print(y.len()); // prints 0
+y.len() == 0;
```
`push` and `pad` are only defined for standard built-in types. For custom types, type-specific versions must be registered:
@@ -1294,17 +1313,18 @@ Object maps are disabled via the [`no_object`] feature.
### Built-in functions
-The following functions (defined in the standard library but excluded if using a [raw `Engine`]) operate on object maps:
+The following methods (defined in the standard library but excluded if using a [raw `Engine`]) operate on object maps:
-| Function | Description |
-| ------------ | ---------------------------------------------------------------------------------------------------------------------------------------- |
-| `has` | does the object map contain a property of a particular name? |
-| `len` | returns the number of properties |
-| `clear` | empties the object map |
-| `mixin` | mixes in all the properties of the second object map to the first (values of properties with the same names replace the existing values) |
-| `+` operator | merges the first object map with the second |
-| `keys` | returns an [array] of all the property names (in random order) |
-| `values` | returns an [array] of all the property values (in random order) |
+| Function | Parameter(s) | Description |
+| ------------ | ----------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------- |
+| `has` | property name | does the object map contain a property of a particular name? |
+| `len` | _none_ | returns the number of properties |
+| `clear` | _none_ | empties the object map |
+| `remove` | property name | removes a certain property and returns it ([`()`] if the property does not exist) |
+| `mixin` | second object map | mixes in all the properties of the second object map to the first (values of properties with the same names replace the existing values) |
+| `+` operator | first object map, second object map | merges the first object map with the second |
+| `keys` | _none_ | returns an [array] of all the property names (in random order) |
+| `values` | _none_ | returns an [array] of all the property values (in random order) |
### Examples
@@ -1322,12 +1342,12 @@ y.a = 42; // access via dot notation
y.baz!$@ = 42; // <- syntax error: only proper variable names allowed in dot notation
y."baz!$@" = 42; // <- syntax error: strings not allowed in dot notation
-print(y.a); // prints 42
+y.a == 42;
-print(y["baz!$@"]); // prints 123.456 - access via index notation
+y["baz!$@"] == 123.456; // access via index notation
-print("baz!$@" in y); // use 'in' to test if a property exists in the object map, prints true
-print("z" in y); // ... prints false
+"baz!$@" in y == true; // use 'in' to test if a property exists in the object map, prints true
+("z" in y) == false;
ts.obj = y; // object maps can be assigned completely (by value copy)
let foo = ts.list.a;
@@ -1352,7 +1372,12 @@ y.has("xyz") == false;
y.xyz == (); // a non-existing property returns '()'
y["xyz"] == ();
-print(y.len()); // prints 3
+y.len() == 3;
+
+y.remove("a") == 1; // remove property
+
+y.len() == 2;
+y.has("a") == false;
for name in keys(y) { // get an array of all the property names via the 'keys' function
print(name);
@@ -1364,7 +1389,7 @@ for val in values(y) { // get an array of all the property values via the 'valu
y.clear(); // empty the object map
-print(y.len()); // prints 0
+y.len() == 0;
```
### Parsing from JSON
diff --git a/src/builtin.rs b/src/builtin.rs
index dfc709c2..e95dac04 100644
--- a/src/builtin.rs
+++ b/src/builtin.rs
@@ -620,15 +620,9 @@ impl Engine<'_> {
#[cfg(not(feature = "no_object"))]
{
- self.register_fn(KEYWORD_PRINT, |x: &mut Map| -> String {
- format!("#{:?}", x)
- });
- self.register_fn(FUNC_TO_STRING, |x: &mut Map| -> String {
- format!("#{:?}", x)
- });
- self.register_fn(KEYWORD_DEBUG, |x: &mut Map| -> String {
- format!("#{:?}", x)
- });
+ self.register_fn(KEYWORD_PRINT, |x: &mut Map| format!("#{:?}", x));
+ self.register_fn(FUNC_TO_STRING, |x: &mut Map| format!("#{:?}", x));
+ self.register_fn(KEYWORD_DEBUG, |x: &mut Map| format!("#{:?}", x));
// Register map access functions
#[cfg(not(feature = "no_index"))]
@@ -874,6 +868,15 @@ impl Engine<'_> {
fn push(list: &mut Array, item: T) {
list.push(Box::new(item));
}
+ fn ins(list: &mut Array, position: INT, item: T) {
+ if position <= 0 {
+ list.insert(0, Box::new(item));
+ } else if (position as usize) >= list.len() - 1 {
+ push(list, item);
+ } else {
+ list.insert(position as usize, Box::new(item));
+ }
+ }
fn pad(list: &mut Array, len: INT, item: T) {
if len >= 0 {
while list.len() < len as usize {
@@ -886,6 +889,7 @@ impl Engine<'_> {
reg_fn2x!(self, "push", push, &mut Array, (), String, Array, ());
reg_fn3!(self, "pad", pad, &mut Array, INT, (), INT, bool, char);
reg_fn3!(self, "pad", pad, &mut Array, INT, (), String, Array, ());
+ reg_fn3!(self, "insert", ins, &mut Array, INT, (), String, Array, ());
self.register_fn("append", |list: &mut Array, array: Array| {
list.extend(array)
@@ -902,12 +906,15 @@ impl Engine<'_> {
reg_fn2x!(self, "push", push, &mut Array, (), i32, i64, u32, u64);
reg_fn3!(self, "pad", pad, &mut Array, INT, (), i8, u8, i16, u16);
reg_fn3!(self, "pad", pad, &mut Array, INT, (), i32, u32, i64, u64);
+ reg_fn3!(self, "insert", ins, &mut Array, INT, (), i8, u8, i16, u16);
+ reg_fn3!(self, "insert", ins, &mut Array, INT, (), i32, i64, u32, u64);
}
#[cfg(not(feature = "no_float"))]
{
reg_fn2x!(self, "push", push, &mut Array, (), f32, f64);
reg_fn3!(self, "pad", pad, &mut Array, INT, (), f32, f64);
+ reg_fn3!(self, "insert", ins, &mut Array, INT, (), f32, f64);
}
self.register_dynamic_fn("pop", |list: &mut Array| {
@@ -920,6 +927,13 @@ impl Engine<'_> {
list.remove(0)
}
});
+ self.register_dynamic_fn("remove", |list: &mut Array, len: INT| {
+ if len < 0 || (len as usize) >= list.len() {
+ ().into_dynamic()
+ } else {
+ list.remove(len as usize)
+ }
+ });
self.register_fn("len", |list: &mut Array| list.len() as INT);
self.register_fn("clear", |list: &mut Array| list.clear());
self.register_fn("truncate", |list: &mut Array, len: INT| {
@@ -935,6 +949,9 @@ impl Engine<'_> {
self.register_fn("has", |map: &mut Map, prop: String| map.contains_key(&prop));
self.register_fn("len", |map: &mut Map| map.len() as INT);
self.register_fn("clear", |map: &mut Map| map.clear());
+ self.register_dynamic_fn("remove", |x: &mut Map, name: String| {
+ x.remove(&name).unwrap_or(().into_dynamic())
+ });
self.register_fn("mixin", |map1: &mut Map, map2: Map| {
map2.into_iter().for_each(|(key, value)| {
map1.insert(key, value);
diff --git a/tests/arrays.rs b/tests/arrays.rs
index 33568460..a41f0a63 100644
--- a/tests/arrays.rs
+++ b/tests/arrays.rs
@@ -16,20 +16,26 @@ fn test_arrays() -> Result<(), EvalAltResult> {
assert_eq!(
engine.eval::(
r"
- let x = [1, 2, 3];
- let y = [4, 5];
- x.append(y);
- x.len()
+ let x = [2, 9];
+ x.insert(-1, 1);
+ x.insert(999, 3);
+
+ let r = x.remove(2);
+
+ let y = [4, 5];
+ x.append(y);
+
+ x.len() + r
"
)?,
- 5
+ 14
);
assert_eq!(
engine.eval::(
r"
- let x = [1, 2, 3];
- x += [4, 5];
- x.len()
+ let x = [1, 2, 3];
+ x += [4, 5];
+ x.len()
"
)?,
5
@@ -38,9 +44,9 @@ fn test_arrays() -> Result<(), EvalAltResult> {
engine
.eval::(
r"
- let x = [1, 2, 3];
- let y = [4, 5];
- x + y
+ let x = [1, 2, 3];
+ let y = [4, 5];
+ x + y
"
)?
.len(),
diff --git a/tests/maps.rs b/tests/maps.rs
index 2002d420..ac591966 100644
--- a/tests/maps.rs
+++ b/tests/maps.rs
@@ -33,6 +33,16 @@ fn test_map_indexing() -> Result<(), EvalAltResult> {
assert!(engine.eval::("let y = #{a: 1, b: 2, c: 3}; 'b' in y")?);
assert!(!engine.eval::(r#"let y = #{a: 1, b: 2, c: 3}; "z" in y"#)?);
+ assert_eq!(
+ engine.eval::(
+ r#"
+ let x = #{a: 1, b: 2, c: 3};
+ let c = x.remove("c");
+ x.len() + c
+ "#
+ )?,
+ 5
+ );
assert_eq!(
engine.eval::(
r"