summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--README.md119
-rw-r--r--lib.c46
-rw-r--r--tests/custom/03_stdlib/64_slice78
3 files changed, 199 insertions, 44 deletions
diff --git a/README.md b/README.md
index fc80c9b..967a8a3 100644
--- a/README.md
+++ b/README.md
@@ -927,14 +927,45 @@ sort(["Bean", "Orange", "Apple"], function(a, b) {
Removes the elements designated by `off` and `len` from the given an array,
and replaces them with the additional arguments passed, if any. Returns the
-last element removed, or `null` if no elements are removed. The array grows or shrinks as necessary.
+last element removed, or `null` if no elements are removed. The array grows
+or shrinks as necessary.
If `off` is negative then it starts that far from the end of the array. If
`len` is omitted, removes everything from `off` onward. If `len` is negative,
removes the elements from `off` onward except for `-len` elements at the end of
the array. If both `off` and `len` are omitted, removes everything.
-#### 6.33. `split(str, sep[, limit])`
+#### 6.33. `slice(arr[, off[, end]])`
+
+Performs a shallow copy of a portion of the source array, as specified by the
+start and end offsets. Returns a new array containing the copied elements.
+The original array is not modified.
+
+The `off` argument specifies the index of the first element to copy from the
+source array while he `end` argument specifies the index of the first element
+to exclude from the returned array, means the copied slice of the source array
+spans from `off` (inclusive) to `end - 1`.
+
+If either `off` or `end` is negative then it starts that far from the end of
+the array. If `off` is omitted it defaults to `0`, if end is omitted, it
+defaults to the length of the source array. Either value is capped to the length
+of the source array.
+
+Returns a new array containing the copied elements, if any.
+
+Returns `null` is the given source argument is not an array value.
+
+```javascript
+slice([1, 2, 3]) // [1, 2, 3]
+slice([1, 2, 3], 1) // [2, 3]
+slice([1, 2, 3], -1) // [3]
+slice([1, 2, 3], -3, -1) // [1, 2]
+slice([1, 2, 3], 10) // []
+slice([1, 2, 3], 2, 1) // []
+slice("invalid", 1, 2) // null
+```
+
+#### 6.34. `split(str, sep[, limit])`
Split the given string using the separator passed as second argument and return
an array containing the resulting pieces.
@@ -952,15 +983,15 @@ split("foo,bar,baz", /[ao]/) // ["f", "", ",b", "r,b", "z"]
split("foo=bar=baz", "=", 2) // ["foo", "bar=baz"]
```
-#### 6.34. `sqrt(x)`
+#### 6.35. `sqrt(x)`
Return the nonnegative square root of x.
-#### 6.35. `srand(n)`
+#### 6.36. `srand(n)`
Seed the PRNG using the given number.
-#### 6.36. `substr(str, off, len)`
+#### 6.37. `substr(str, off, len)`
Extracts a substring out of `str` and returns it. First character is at offset
zero. If `off` is negative, starts that far back from the end of the string.
@@ -977,7 +1008,7 @@ substr(s, -4); // tree
substr(s, -4, 2); // tr
```
-#### 6.37. `time()`
+#### 6.38. `time()`
Returns the current UNIX epoch.
@@ -985,7 +1016,7 @@ Returns the current UNIX epoch.
time(); // 1598043054
```
-#### 6.38. `trim()`
+#### 6.39. `trim()`
Trim any of the specified characters in `c` from the start and end of `str`.
If the second argument is omitted, trims the characters, ` ` (space), `\t`,
@@ -996,14 +1027,14 @@ ltrim(" foo \n") // "foo"
ltrim("--bar--", "-") // "bar"
```
-#### 6.39. `type(x)`
+#### 6.40. `type(x)`
Returns the type of the given value as string which might be one of
`"function"`, `"object"`, `"array"`, `"double"`, `"int"` or `"bool"`.
Returns `null` when no value or `null` is passed.
-#### 6.40. `uchr(n1, ...)`
+#### 6.41. `uchr(n1, ...)`
Converts each given numeric value to an utf8 escape sequence and returns the
resulting string. Invalid numeric values or values outside the range `0` ..
@@ -1014,17 +1045,17 @@ uchr(0x2600, 0x26C6, 0x2601); // "☀⛆☁"
uchr(-1, 0x20ffff, "foo"); // "���"
```
-#### 6.41. `uc(str)`
+#### 6.42. `uc(str)`
Converts the given string to uppercase and return the resulting string.
Returns `null` if the given argument could not be converted to a string.
-#### 6.42. `unshift(arr, v1, ...)`
+#### 6.43. `unshift(arr, v1, ...)`
Add the given values to the beginning of the array passed as first argument.
Returns the last value added to the array.
-#### 6.43. `values(obj)`
+#### 6.44. `values(obj)`
Returns an array containing all values of the given object. Returns `null` if
no object was passed.
@@ -1033,7 +1064,7 @@ no object was passed.
values({ foo: true, bar: false }); // [true, false]
```
-#### 6.44. `printf(fmt, ...)`
+#### 6.45. `printf(fmt, ...)`
Formats the given arguments according to the given format string and outputs the
result to stdout.
@@ -1077,14 +1108,14 @@ well.
%}
```
-#### 6.45. `sprintf(fmt, ...)`
+#### 6.46. `sprintf(fmt, ...)`
Formats the given arguments according to the given format string and returns the
resulting string.
See `printf()` for details.
-#### 6.46. `match(str, /pattern/)`
+#### 6.47. `match(str, /pattern/)`
Match the given string against the regular expression pattern specified as
second argument.
@@ -1100,7 +1131,7 @@ match("foobarbaz", /b.(.)/) // ["bar", "r"]
match("foobarbaz", /b.(.)/g) // [["bar", "r"], ["baz", "z"]]
```
-#### 6.47. `replace(str, /pattern/, replace[, limit])`
+#### 6.48. `replace(str, /pattern/, replace[, limit])`
Replace occurences of the specified pattern in the string passed as first
argument. The pattern value may be either a regular expression or a plain
@@ -1138,7 +1169,7 @@ replace("aaaaa", "a", "x", 3) // xxxaa
replace("foo bar baz", /[ao]/g, "x", 3) // fxx bxr baz
```
-#### 6.48. `json(str)`
+#### 6.49. `json(str)`
Parse the given string as JSON and return the resulting value. Throws an
exception on parse errors, trailing garbage or premature EOF.
@@ -1148,7 +1179,7 @@ json('{"a":true, "b":123}') // { "a": true, "b": 123 }
json('[1,2,') // Throws exception
```
-#### 6.49. `include(path[, scope])`
+#### 6.50. `include(path[, scope])`
Evaluate and include the file at the given path and optionally override the
execution scope with the given scope object.
@@ -1191,14 +1222,14 @@ include("./untrusted.uc", proto({
}, {}))
```
-#### 6.50. `warn(x, ...)`
+#### 6.51. `warn(x, ...)`
Print any of the given values to stderr. Arrays and objects are converted to
their JSON representation.
Returns the amount of bytes printed.
-#### 6.51. `system(command, timeout)`
+#### 6.52. `system(command, timeout)`
Executes the given command, waits for completion and returns the resulting
exit code.
@@ -1228,7 +1259,7 @@ system(["/usr/bin/date", "+%s"]); // prints the UNIX timestamp to stdou
system("sleep 3 && echo 'Success'", 1000); // returns -9
```
-#### 6.52. `trace(level)`
+#### 6.53. `trace(level)`
Enables or disables VM opcode tracing. When invoked with a positive non-zero
level, opcode tracing is enabled and debug information is printed to stderr
@@ -1241,7 +1272,7 @@ implementation might provide different different verbosity levels or treat
the level argument as bit mask to enable or disable individual debug
elements.
-#### 6.53. `proto(val[, proto])`
+#### 6.54. `proto(val[, proto])`
Get or set the prototype of the array or object value `val`.
@@ -1254,17 +1285,17 @@ set as prototype on the array or object in `val`.
Throws an exception if the given prototype value is not an object.
-#### 6.54. `sleep(milliseconds)`
+#### 6.55. `sleep(milliseconds)`
Pause execution for the given amount of milliseconds. Returns `false` if
an invalid value was passed, otherwise `true`.
-#### 6.55. `assert(cond[, message])`
+#### 6.56. `assert(cond[, message])`
Raise an exception with the given `message` parameter if the value in `cond`
is not truish. When `message` is omitted, the default value is `Assertion failed`.
-#### 6.56. `render(path_or_func[, scope_or_fnarg1 [, fnarg2 [, ...]]])`
+#### 6.57. `render(path_or_func[, scope_or_fnarg1 [, fnarg2 [, ...]]])`
When invoked with a string value as first argument, the function acts like
like `include()` but captures the output of the included file as string and
@@ -1277,7 +1308,7 @@ given function and passes all subsequent arguments to it. Any output
called function is captured and returned as string. The return value of the
called function is discarded.
-#### 6.57. `regexp(source[, flags])`
+#### 6.58. `regexp(source[, flags])`
Construct a regular expression instance from the given `source` pattern string
and any flags optionally specified by the `flags` argument.
@@ -1296,7 +1327,7 @@ regexp('foo.*bar', 'x'); // throws "Type error: Unrecognized flag character '
regexp('foo.*('); // throws "Syntax error: Unmatched ( or \("
```
-#### 6.58. `wildcard(subject, pattern[, nocase])`
+#### 6.59. `wildcard(subject, pattern[, nocase])`
Match the given subject against the supplied wildcard (file glob) pattern.
@@ -1306,7 +1337,7 @@ a string before being matched.
Returns `true` when the subject matches the pattern or `false` when not.
-#### 6.59. `sourcepath([depth [, dironly]])`
+#### 6.60. `sourcepath([depth [, dironly]])`
Determine the path of the source file currently being executed by ucode.
@@ -1325,7 +1356,7 @@ file path.
If `depth` exceeds the size of the call stack, the function returns `null`
as well.
-#### 6.60. `min([val1 [, val2 [, ...]]])`
+#### 6.61. `min([val1 [, val2 [, ...]]])`
Return the smallest value among all parameters passed to the function.
The function does a `val1 < val2` comparison internally, which means that
@@ -1342,7 +1373,7 @@ min("def", "abc", "ghi"); // "abc"
min(true, false); // false
```
-#### 6.61. `max([val1 [, val2 [, ...]]])`
+#### 6.62. `max([val1 [, val2 [, ...]]])`
Return the largest value among all parameters passed to the function.
The function does a `val1 > val2` comparison internally, which means that
@@ -1359,7 +1390,7 @@ max("def", "abc", "ghi"); // "ghi"
max(true, false); // true
```
-#### 6.62. `b64dec(str)`
+#### 6.63. `b64dec(str)`
Decodes the given base64 encoded string and returns the decoded result, any
whitespace in the input string is ignored.
@@ -1375,7 +1406,7 @@ b64dec(123); // null
b64dec("XXX"); // null
```
-#### 6.63. `b64enc(str)`
+#### 6.64. `b64enc(str)`
Encodes the given string into base64 and returns the resulting encoded
string.
@@ -1387,7 +1418,7 @@ b64enc("This is a test"); // "VGhpcyBpcyBhIHRlc3Q="
b64enc(123); // null
```
-#### 6.64. `uniq(array)`
+#### 6.65. `uniq(array)`
Returns a new array containing all unique values of the given input
array. The order is preserved, that is subsequent duplicate values
@@ -1400,7 +1431,7 @@ uniq([ 1, true, "foo", 2, true, "bar", "foo" ]); // [ 1, true, "foo", 2, "bar" ]
uniq("test"); // null
```
-#### 6.65. `localtime([epoch])`
+#### 6.66. `localtime([epoch])`
Return the given epoch timestamp (or now, if omitted) as a dictionary
containing broken-down date and time information according to the local
@@ -1437,13 +1468,13 @@ localtime(1647953502);
// }
```
-#### 6.66. `gmtime([epoch])`
+#### 6.67. `gmtime([epoch])`
Like `localtime()` but interpreting the given epoch value as UTC time.
See `localtime()` for details on the return value.
-#### 6.67. `timelocal(datetimespec)`
+#### 6.68. `timelocal(datetimespec)`
Performs the inverse operation of `localtime()` by taking a broken-down
date and time dictionary and transforming it into an epoch value according
@@ -1462,14 +1493,14 @@ timelocal({ "sec": 42, "min": 51, "hour": 13, "mday": 22, "mon": 3, "year": 2022
// 1647953502
```
-#### 6.68. `timegm(datetimespec)`
+#### 6.69. `timegm(datetimespec)`
Like `timelocal()` but interpreting the given date time specification as
UTC time.
See `timelocal()` for details.
-#### 6.69. `clock([monotonic])`
+#### 6.70. `clock([monotonic])`
Reads the current second and microsecond value of the system clock.
@@ -1492,7 +1523,7 @@ clock(); // [ 1647954926, 798269464 ]
clock(true); // [ 474751, 527959975 ]
```
-#### 6.70. `hexdec(hexstring[, skipchars])`
+#### 6.71. `hexdec(hexstring[, skipchars])`
The `hexdec()` function decodes the given hexadecimal digit string into
a byte string, optionally skipping specified characters.
@@ -1510,7 +1541,7 @@ hexdec("48656c6c6f20776f726c64210a"); // "Hello world!\n"
hexdec("44:55:66:77:33:44", ":"); // "DUfw3D"
```
-#### 6.71. `hexenc(val)`
+#### 6.72. `hexenc(val)`
The `hexenc()` function encodes the given byte string into a hexadecimal
digit string, converting the input value to a string if needed.
@@ -1521,7 +1552,7 @@ Returns the encoded hexadecimal digit string.
hexenc("Hello world!\n"); // "48656c6c6f20776f726c64210a"
```
-#### 6.72. `gc([operation[, argument]])`
+#### 6.73. `gc([operation[, argument]])`
The `gc()` function allows interaction with the mark and sweep garbage
collector of the running ucode virtual machine.
@@ -1546,7 +1577,7 @@ If the `operation` argument is omitted, the default is `collect`.
Returns `null` if a non-string `operation` value is given.
-#### 6.73. `loadstring(code[, options])`
+#### 6.74. `loadstring(code[, options])`
Compiles the given code string into a ucode program and returns the resulting
program entry function. The optinal `options` dictionary allows overriding
@@ -1587,7 +1618,7 @@ let fn2 = loadstring("return 1 + 2;", { raw_mode: true });
fn2(); // 3
```
-#### 6.74. `loadfile(path[, options])`
+#### 6.75. `loadfile(path[, options])`
Compiles the given file into a ucode program and returns the resulting program
entry function.
@@ -1602,7 +1633,7 @@ Throws an exception on compilation or file i/o errors.
loadfile("./templates/example.uc"); // function main() { ... }
```
-#### 6.75. `call(fn[, ctx[, scope[, arg1[, ...]]]])`
+#### 6.76. `call(fn[, ctx[, scope[, arg1[, ...]]]])`
Calls the given function value with a modified environment. The given `ctx`
argument is used as `this` context for the invoked function and the given
diff --git a/lib.c b/lib.c
index 51b99bf..834388a 100644
--- a/lib.c
+++ b/lib.c
@@ -1019,6 +1019,51 @@ uc_splice(uc_vm_t *vm, size_t nargs)
}
static uc_value_t *
+uc_slice(uc_vm_t *vm, size_t nargs)
+{
+ uc_value_t *arr = uc_fn_arg(0);
+ uc_value_t *sv = uc_fn_arg(1);
+ uc_value_t *ev = uc_fn_arg(2);
+ uc_value_t *res = NULL;
+ int64_t off, end;
+ size_t len;
+
+ if (ucv_type(arr) != UC_ARRAY)
+ return NULL;
+
+ len = ucv_array_length(arr);
+ off = sv ? ucv_to_integer(sv) : 0;
+ end = ev ? ucv_to_integer(ev) : (int64_t)len;
+
+ if (off < 0) {
+ off = len + off;
+
+ if (off < 0)
+ off = 0;
+ }
+ else if ((uint64_t)off > len) {
+ off = len;
+ }
+
+ if (end < 0) {
+ end = len + end;
+
+ if (end < 0)
+ end = 0;
+ }
+ else if ((uint64_t)end > len) {
+ end = len;
+ }
+
+ res = ucv_array_new(vm);
+
+ while (off < end)
+ ucv_array_push(res, ucv_get(ucv_array_get(arr, off++)));
+
+ return res;
+}
+
+static uc_value_t *
uc_split(uc_vm_t *vm, size_t nargs)
{
uc_value_t *str = uc_fn_arg(0);
@@ -3845,6 +3890,7 @@ const uc_function_list_t uc_stdlib_functions[] = {
{ "shift", uc_shift },
{ "sort", uc_sort },
{ "splice", uc_splice },
+ { "slice", uc_slice },
{ "split", uc_split },
{ "substr", uc_substr },
{ "time", uc_time },
diff --git a/tests/custom/03_stdlib/64_slice b/tests/custom/03_stdlib/64_slice
new file mode 100644
index 0000000..c8db303
--- /dev/null
+++ b/tests/custom/03_stdlib/64_slice
@@ -0,0 +1,78 @@
+The `slice()` function returns a shallow copy of a portion of the source
+array, as specified by the start and end offsets. The original array is
+not modified.
+
+If start is omitted or null, it defaults to `0`. If end is omitted or null,
+it defaults to the length of the source array.
+
+If either of the offsets is negative, it is treated as counting towards the
+end of the array. If either value exceeds the array length, it is capped to
+the length of the array.
+
+Returns a new array containing the elements copied from the source array.
+
+Returns `null` if the given input array value is not an array.
+
+
+-- Testcase --
+{%
+ let arr = [ 1, 2, 3, 4, 5 ];
+
+ print(join("\n", [
+ // copy all items
+ slice(arr),
+
+ // copy item 3 onwards
+ slice(arr, 3),
+
+ // copy item 2 and 3
+ slice(arr, 1, 3),
+
+ // copy last two items
+ slice(arr, -2),
+
+ // copy items 3 and 4
+ slice(arr, -3, -1)
+ ]), "\n");
+%}
+-- End --
+
+-- Expect stdout --
+[ 1, 2, 3, 4, 5 ]
+[ 4, 5 ]
+[ 2, 3 ]
+[ 4, 5 ]
+[ 3, 4 ]
+-- End --
+
+
+Supplying an invalid array will yield `null`.
+
+-- Testcase --
+{%
+ printf("%.J\n", slice("not_an_array", 0, 1));
+%}
+-- End --
+
+-- Expect stdout --
+null
+-- End --
+
+
+Invalid, non-numeric offset or index values are treated as 0.
+
+-- Testcase --
+{%
+ let arr = [ 1, 2, 3, 4, 5 ];
+
+ print(join("\n", [
+ slice(arr, "foo"),
+ slice(arr, "foo", "bar")
+ ]), "\n");
+%}
+-- End --
+
+-- Expect stdout --
+[ 1, 2, 3, 4, 5 ]
+[ ]
+-- End --