diff options
author | Jo-Philipp Wich <jo@mein.io> | 2023-07-05 09:04:09 +0200 |
---|---|---|
committer | Jo-Philipp Wich <jo@mein.io> | 2023-07-12 10:45:22 +0200 |
commit | 5309294666c287902e08fa61d3f07ddc03e9f2a3 (patch) | |
tree | 882b622417396432814184365440404b84044a98 | |
parent | b0f2f9002bcfd9b562b030c78406305ce6b3074e (diff) |
lib: add JSDoc documentation
Add JSDoc documentation blocks to all exported core functions.
Signed-off-by: Jo-Philipp Wich <jo@mein.io>
-rw-r--r-- | lib.c | 1721 |
1 files changed, 1721 insertions, 0 deletions
@@ -14,6 +14,15 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +/** + * Builtin functions. + * + * The core namespace is not an actual module but refers to the set of + * builtin functions and properties available to `ucode` scripts. + * + * @module core + */ + #include <stdio.h> #include <stdlib.h> #include <stdarg.h> @@ -293,12 +302,78 @@ uc_print_common(uc_vm_t *vm, size_t nargs, FILE *fh) } +/** + * Print any of the given values to stdout. + * + * The `print()` function writes a string representation of each given argument + * to stdout and returns the amount of bytes written. + * + * String values are printed as-is, integer and double values are printed in + * decimal notation, boolean values are printed as `true` or `false` while + * arrays and objects are converted to their JSON representation before being + * written to the standard output. The `null` value is represented by an empty + * string so `print(null)` would print nothing. Resource values are printed in + * the form `<type address>`, e.g. `<fs.file 0x7f60f0981760>`. + * + * If resource, array or object values contain a `tostring()` function in their + * prototypes, then this function is invoked to obtain an alternative string + * representation of the value. + * + * Examples: + * + * ```javascript + * print(1 != 2); // Will print 'true' + * print(0xff); // Will print '255' + * print(2e3); // Will print '2000' + * print(null); // Will print nothing + * print({ hello: true, world: 123 }); // Will print '{ "hello": true, "world": 123 }' + * print([1,2,3]); // Will print '[ 1, 2, 3 ]' + * + * print(proto({ foo: "bar" }, // Will print 'MyObj' + * { tostring: () => "MyObj" })); // instead of '{ "foo": "bar" }' + * + * ``` + * + * Returns the amount of bytes printed. + * + * @function module:core#print + * + * @param {...*} values + * Arbitrary values to print + * + * @returns {number} + */ static uc_value_t * uc_print(uc_vm_t *vm, size_t nargs) { return uc_print_common(vm, nargs, vm->output); } +/** + * Determine the length of the given object, array or string. + * + * Returns the length of the given value. + * + * - For strings, the length is the amount of bytes within the string + * - For arrays, the length is the amount of array elements + * - For objects, the length is defined as the amount of keys + * + * Returns `null` if the given argument is not an object, array or string. + * + * @function module:core#length + * + * @param {Object|Array|string} x - The input object, array, or string. + * + * @returns {number|null} - The length of the input. + * + * @example + * length("test") // 4 + * length([true, false, null, 123, "test"]) // 5 + * length({foo: true, bar: 123, baz: "test"}) // 3 + * length({}) // 0 + * length(true) // null + * length(10.0) // null + */ static uc_value_t * uc_length(uc_vm_t *vm, size_t nargs) { @@ -391,12 +466,64 @@ uc_index(uc_vm_t *vm, size_t nargs, bool right) } } +/** + * Finds the given value passed as the second argument within the array or + * string specified in the first argument. + * + * Returns the first matching array index or first matching string offset or + * `-1` if the value was not found. + * + * Returns `null` if the first argument was neither an array nor a string. + * + * @function module:core#index + * + * @param {Array|string} arr_or_str + * The array or string to search for the value. + * + * @param {*} needle + * The value to find within the array or string. + * + * @returns {number|null} + * + * @example + * index("Hello hello hello", "ll") // 2 + * index([ 1, 2, 3, 1, 2, 3, 1, 2, 3 ], 2) // 1 + * index("foo", "bar") // -1 + * index(["Red", "Blue", "Green"], "Brown") // -1 + * index(123, 2) // null + */ static uc_value_t * uc_lindex(uc_vm_t *vm, size_t nargs) { return uc_index(vm, nargs, false); } +/** + * Finds the given value passed as the second argument within the array or + * string specified in the first argument. + * + * Returns the last matching array index or last matching string offset or + * `-1` if the value was not found. + * + * Returns `null` if the first argument was neither an array nor a string. + * + * @function module:core#rindex + * + * @param {Array|string} arr_or_str + * The array or string to search for the value. + * + * @param {*} needle + * The value to find within the array or string. + * + * @returns {number|null} + * + * @example + * rindex("Hello hello hello", "ll") // 14 + * rindex([ 1, 2, 3, 1, 2, 3, 1, 2, 3 ], 2) // 7 + * rindex("foo", "bar") // -1 + * rindex(["Red", "Blue", "Green"], "Brown") // -1 + * rindex(123, 2) // null + */ static uc_value_t * uc_rindex(uc_vm_t *vm, size_t nargs) { @@ -426,6 +553,26 @@ assert_mutable_array(uc_vm_t *vm, uc_value_t *val) return assert_mutable(vm, val); } +/** + * Pushes the given argument(s) to the given array. + * + * Returns the last pushed value. + * + * @function module:core#push + * + * @param {Array} arr + * The array to push values to. + * + * @param {...*} [values] + * The values to push. + * + * @returns {*} + * + * @example + * let x = [ 1, 2, 3 ]; + * push(x, 4, 5, 6); // 6 + * print(x, "\n"); // [ 1, 2, 3, 4, 5, 6 ] + */ static uc_value_t * uc_push(uc_vm_t *vm, size_t nargs) { @@ -444,6 +591,23 @@ uc_push(uc_vm_t *vm, size_t nargs) return ucv_get(item); } +/** + * Pops the first item from the given array and returns it. + * + * Returns `null` if the array was empty or if a non-array argument was passed. + * + * @function module:core#pop + * + * @param {Array} arr + * The input array. + * + * @returns {*} + * + * @example + * let x = [ 1, 2, 3 ]; + * pop(x); // 3 + * print(x, "\n"); // [ 1, 2 ] + */ static uc_value_t * uc_pop(uc_vm_t *vm, size_t nargs) { @@ -455,6 +619,23 @@ uc_pop(uc_vm_t *vm, size_t nargs) return ucv_array_pop(arr); } +/** + * Pops the first item from the given array and returns it. + * + * Returns `null` if the array was empty or if a non-array argument was passed. + * + * @function module:core#shift + * + * @param {Array} arr + * The array from which to pop the first item. + * + * @returns {*} + * + * @example + * let x = [ 1, 2, 3 ]; + * shift(x); // 1 + * print(x, "\n"); // [ 2, 3 ] + */ static uc_value_t * uc_shift(uc_vm_t *vm, size_t nargs) { @@ -466,6 +647,26 @@ uc_shift(uc_vm_t *vm, size_t nargs) return ucv_array_shift(arr); } +/** + * Add the given values to the beginning of the array passed via first argument. + * + * Returns the last value added to the array. + * + * @function module:core#unshift + * + * @param {Array} arr + * The array to which the values will be added. + * + * @param {...*} + * Values to add. + * + * @returns {*} + * + * @example + * let x = [ 3, 4, 5 ]; + * unshift(x, 1, 2); // 2 + * print(x, "\n"); // [ 1, 2, 3, 4, 5 ] + */ static uc_value_t * uc_unshift(uc_vm_t *vm, size_t nargs) { @@ -484,6 +685,24 @@ uc_unshift(uc_vm_t *vm, size_t nargs) return (nargs > 1) ? ucv_get(uc_fn_arg(nargs - 1)) : NULL; } +/** + * Converts each given numeric value to a byte and return the resulting string. + * Invalid numeric values or values < 0 result in `\0` bytes, values larger than + * 255 are truncated to 255. + * + * Returns a new strings consisting of the given byte values. + * + * @function module:core#chr + * + * @param {...number} n1 + * The numeric values. + * + * @returns {string} + * + * @example + * chr(65, 98, 99); // "Abc" + * chr(-1, 300); // string consisting of an `0x0` and a `0xff` byte + */ static uc_value_t * uc_chr(uc_vm_t *vm, size_t nargs) { @@ -514,6 +733,20 @@ uc_chr(uc_vm_t *vm, size_t nargs) return rv; } +/** + * Raise an exception with the given message and abort execution. + * + * @function module:core#die + * + * @param {string} msg + * The error message. + * + * @throws {Error} + * The error with the given message. + * + * @example + * die(msg); + */ static uc_value_t * uc_die(uc_vm_t *vm, size_t nargs) { @@ -531,6 +764,28 @@ uc_die(uc_vm_t *vm, size_t nargs) return NULL; } +/** + * Check whether the given key exists within the given object value. + * + * Returns `true` if the given key is present within the object passed as the + * first argument, otherwise `false`. + * + * @function module:core#exists + * + * @param {Object} obj + * The input object. + * + * @param {string} key + * The key to check for existence. + * + * @returns {boolean} + * + * @example + * let x = { foo: true, bar: false, qrx: null }; + * exists(x, 'foo'); // true + * exists(x, 'qrx'); // true + * exists(x, 'baz'); // false + */ static uc_value_t * uc_exists(uc_vm_t *vm, size_t nargs) { @@ -552,6 +807,20 @@ uc_exists(uc_vm_t *vm, size_t nargs) return ucv_boolean_new(found); } +/** + * Terminate the interpreter with the given exit code. + * + * This function does not return. + * + * @function module:core#exit + * + * @param {number} n + * The exit code. + * + * @example + * exit(); + * exit(5); + */ static uc_value_t * uc_exit(uc_vm_t *vm, size_t nargs) { @@ -563,6 +832,19 @@ uc_exit(uc_vm_t *vm, size_t nargs) return NULL; } +/** + * Query an environment variable or then entire environment. + * + * Returns the value of the given environment variable, or - if omitted - a + * dictionary containing all environment variables. + * + * @function module:core#getenv + * + * @param {string} [name] + * The name of the environment variable. + * + * @returns {string|object} + */ static uc_value_t * uc_getenv(uc_vm_t *vm, size_t nargs) { @@ -596,6 +878,42 @@ uc_getenv(uc_vm_t *vm, size_t nargs) return rv; } +/** + * Filter the array passed as the first argument by invoking the function + * specified in the second argument for each array item. + * + * If the invoked function returns a truthy result, the item is retained, + * otherwise, it is dropped. The filter function is invoked with three + * arguments: + * + * 1. The array value + * 2. The current index + * 3. The array being filtered + * + * Returns a new array containing only retainted items, in the same order as + * the input array. + * + * @function module:core#filter + * + * @param {Array} arr + * The input array. + * + * @param {Function} fn + * The filter function. + * + * @returns {Array} + * + * @example + * // filter out any empty string: + * a = filter(["foo", "", "bar", "", "baz"], length) + * // a = ["foo", "bar", "baz"] + * + * // filter out any non-number type: + * a = filter(["foo", 1, true, null, 2.2], function(v) { + * return (type(v) == "int" || type(v) == "double"); + * }); + * // a = [1, 2.2] + */ static uc_value_t * uc_filter(uc_vm_t *vm, size_t nargs) { @@ -633,6 +951,19 @@ uc_filter(uc_vm_t *vm, size_t nargs) return arr; } +/** + * Converts the given hexadecimal string into a number. + * + * Returns the resulting integer value or `NaN` if the input value cannot be + * interpreted as hexadecimal number. + * + * @function module:core#hex + * + * @param {*} x + * The hexadecimal string to be converted. + * + * @returns {number} + */ static uc_value_t * uc_hex(uc_vm_t *vm, size_t nargs) { @@ -653,6 +984,18 @@ uc_hex(uc_vm_t *vm, size_t nargs) return ucv_int64_new(n); } +/** + * Converts the given value to an integer. + * + * Returns `NaN` if the value is not convertible. + * + * @function module:core#int + * + * @param {*} x + * The value to be converted to an integer. + * + * @returns {number} + */ static uc_value_t * uc_int(uc_vm_t *vm, size_t nargs) { @@ -679,6 +1022,22 @@ uc_int(uc_vm_t *vm, size_t nargs) return ucv_int64_new(n); } +/** + * Joins the array passed as the second argument into a string, using the + * separator passed in the first argument as glue. + * + * Returns `null` if the second argument is not an array. + * + * @function module:core#join + * + * @param {string} sep + * The separator to be used in joining the array elements. + * + * @param {Array} arr + * The array to be joined into a string. + * + * @returns {string|null} + */ static uc_value_t * uc_join(uc_vm_t *vm, size_t nargs) { @@ -702,6 +1061,19 @@ uc_join(uc_vm_t *vm, size_t nargs) return ucv_stringbuf_finish(buf); } +/** + * Enumerates all object key names. + * + * Returns an array of all key names present in the passed object. + * Returns `null` if the given argument is not an object. + * + * @function module:core#keys + * + * @param {object} obj + * The object from which to retrieve the key names. + * + * @returns {Array|null} + */ static uc_value_t * uc_keys(uc_vm_t *vm, size_t nargs) { @@ -721,6 +1093,22 @@ uc_keys(uc_vm_t *vm, size_t nargs) return arr; } +/** + * Convert the given string to lowercase and return the resulting string. + * + * Returns `null` if the given argument could not be converted to a string. + * + * @function module:core#lc + * + * @param {string} s + * The input string. + * + * @returns {string|null} + * The lowercase string. + * + * @example + * lc("HeLLo WoRLd!"); // "hello world!" + */ static uc_value_t * uc_lc(uc_vm_t *vm, size_t nargs) { @@ -742,6 +1130,32 @@ uc_lc(uc_vm_t *vm, size_t nargs) return rv; } +/** + * Transform the array passed as the first argument by invoking the function + * specified in the second argument for each array item. + * + * Returns a new array of the same length as the input array containing the + * transformed values. + * + * @function module:core#map + * + * @param {Array} arr + * The input array. + * + * @param {Function} fn + * The mapping function. + * + * @returns {Array} + * + * @example + * // turn into an array of string lengths: + * a = map(["Apple", "Banana", "Bean"], length); + * // a = [5, 6, 4] + * + * // map to type names: + * a = map(["foo", 1, true, null, 2.2], type); + * // a = ["string", "int", "bool", null, "double"] + */ static uc_value_t * uc_map(uc_vm_t *vm, size_t nargs) { @@ -776,6 +1190,37 @@ uc_map(uc_vm_t *vm, size_t nargs) return arr; } +/** + * Without further arguments, this function returns the byte value of the first + * character in the given string. + * + * If an offset argument is supplied, the byte value of the character at this + * position is returned. If an invalid index is supplied, the function will + * return `null`. Negative index entries are counted towards the end of the + * string, e.g. `-2` will return the value of the second last character. + * + * Returns the byte value of the character. + * Returns `null` if the offset is invalid or if the input is not a string. + * + * @function module:core#ord + * + * @param {string} s + * The input string. + * + * @param {number} [offset] + * The offset of the character. + * + * @returns {number|null} + * + * @example + * ord("Abc"); // 65 + * ord("Abc", 0); // 65 + * ord("Abc", 1); // 98 + * ord("Abc", 2); // 99 + * ord("Abc", 10); // null + * ord("Abc", -10); // null + * ord("Abc", "nan"); // null + */ static uc_value_t * uc_ord(uc_vm_t *vm, size_t nargs) { @@ -806,6 +1251,21 @@ uc_ord(uc_vm_t *vm, size_t nargs) return ucv_int64_new((uint8_t)str[n]); } +/** + * Query the type of the given value. + * + * Returns the type of the given value as a string which might be one of + * `"function"`, `"object"`, `"array"`, `"double"`, `"int"`, or `"bool"`. + * + * Returns `null` when no value or `null` is passed. + * + * @function module:core#type + * + * @param {*} x + * The value to determine the type of. + * + * @returns {string|null} + */ static uc_value_t * uc_type(uc_vm_t *vm, size_t nargs) { @@ -831,6 +1291,27 @@ uc_type(uc_vm_t *vm, size_t nargs) } } +/** + * Reverse the order of the given input array or string. + * + * If an array is passed, returns the array in reverse order. + * If a string is passed, returns the string with the sequence of the characters + * reversed. + * + * Returns the reversed array or string. + * Returns `null` if neither an array nor a string were passed. + * + * @function module:core#reverse + * + * @param {Array|string} arr_or_str + * The input array or string. + * + * @returns {Array|string|null} + * + * @example + * reverse([1, 2, 3]); // [ 3, 2, 1 ] + * reverse("Abc"); // "cbA" + */ static uc_value_t * uc_reverse(uc_vm_t *vm, size_t nargs) { @@ -971,6 +1452,35 @@ object_sort_fn(const void *k1, const void *k2) return res; } +/** + * Sort the given array according to the given sort function. + * If no sort function is provided, a default ascending sort order is applied. + * + * The input array is sorted in-place, no copy is made. + * + * The custom sort function is repeatedly called until the entire array is + * sorted. It will receive two values as arguments and should return a value + * lower than, larger than or equal to zero depending on whether the first + * argument is smaller, larger or equal to the second argument respectively. + * + * Returns the sorted input array. + * + * @function module:core#sort + * + * @param {Array} arr + * The input array to be sorted. + * + * @param {Function} [fn] + * The sort function. + * + * @returns {Array} + * + * @example + * sort([8, 1, 5, 9]) // [1, 5, 8, 9] + * sort(["Bean", "Orange", "Apple"], function(a, b) { + * return length(a) - length(b); + * }) // ["Bean", "Apple", "Orange"] + */ static uc_value_t * uc_sort(uc_vm_t *vm, size_t nargs) { @@ -999,6 +1509,35 @@ uc_sort(uc_vm_t *vm, size_t nargs) return sort_ctx.ex ? NULL : ucv_get(val); } +/** + * Removes the elements designated by `off` and `len` from the given array, + * and replaces them with the additional arguments passed, if any. + * + * The array grows or shrinks as necessary. + * + * Returns the last element removed, or `null` if no elements are removed. + * + * @function module:core#splice + * + * @param {Array} arr + * The input array to be modified. + * + * @param {number} off + * The index to start removing elements. + * + * @param {number} [len] + * The number of elements to remove. + * + * @param {...*} [elements] + * The elements to insert. + * + * @returns {*} + * + * @example + * let x = [ 1, 2, 3, 4 ]; + * splice(x, 1, 2, "a", "b", "c"); // 3 + * print(x, "\n"); // [ 1, "a", "b", "c", 4 ] + */ static uc_value_t * uc_splice(uc_vm_t *vm, size_t nargs) { @@ -1072,6 +1611,35 @@ uc_splice(uc_vm_t *vm, size_t nargs) return ucv_get(arr); } +/** + * Performs a shallow copy of a portion of the source array, as specified by + * the start and end offsets. The original array is not modified. + * + * Returns a new array containing the copied elements, if any. + * Returns `null` if the given source argument is not an array value. + * + * @function module:core#slice + * + * @param {Array} arr + * The source array to be copied. + * + * @param {number} [off] + * The index of the first element to copy. + * + * @param {number} [end] + * The index of the first element to exclude from the returned array. + * + * @returns {Array} + * + * @example + * 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 + */ static uc_value_t * uc_slice(uc_vm_t *vm, size_t nargs) { @@ -1117,6 +1685,37 @@ uc_slice(uc_vm_t *vm, size_t nargs) return res; } +/** + * Split the given string using the separator passed as the second argument + * and return an array containing the resulting pieces. + * + * If a limit argument is supplied, the resulting array contains no more than + * the given amount of entries, that means the string is split at most + * `limit - 1` times total. + * + * The separator may either be a plain string or a regular expression. + * + * Returns a new array containing the resulting pieces. + * + * @function module:core#split + * + * @param {string} str + * The input string to be split. + * + * @param {string|RegExp} sep + * The separator. + * + * @param {number} [limit] + * The limit on the number of splits. + * + * @returns {Array} + * + * @example + * split("foo,bar,baz", ",") // ["foo", "bar", "baz"] + * split("foobar", "") // ["f", "o", "o", "b", "a", "r"] + * split("foo,bar,baz", /[ao]/) // ["f", "", ",b", "r,b", "z"] + * split("foo=bar=baz", "=", 2) // ["foo", "bar=baz"] + */ static uc_value_t * uc_split(uc_vm_t *vm, size_t nargs) { @@ -1215,6 +1814,37 @@ out: return arr; } +/** + * 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. + * - If `len` is omitted, returns everything through the end of the string. + * - If `len` is negative, leaves that many characters off the string end. + * + * Returns the extracted substring. + * + * @function module:core#substr + * + * @param {string} str + * The input string. + * + * @param {number} off + * The starting offset. + * + * @param {number} [len] + * The length of the substring. + * + * @returns {string} + * + * @example + * s = "The black cat climbed the green tree"; + * substr(s, 4, 5); // black + * substr(s, 4, -11); // black cat climbed the + * substr(s, 14); // climbed the green tree + * substr(s, -4); // tree + * substr(s, -4, 2); // tr + */ static uc_value_t * uc_substr(uc_vm_t *vm, size_t nargs) { @@ -1279,6 +1909,16 @@ uc_substr(uc_vm_t *vm, size_t nargs) return ucv_string_new_length(p + ofs, sublen); } +/** + * Returns the current UNIX epoch. + * + * @function module:core#time + * + * @returns {number} + * + * @example + * time(); // 1598043054 + */ static uc_value_t * uc_time(uc_vm_t *vm, size_t nargs) { @@ -1287,6 +1927,23 @@ uc_time(uc_vm_t *vm, size_t nargs) return ucv_int64_new((int64_t)t); } +/** + * Converts the given string to uppercase and returns the resulting string. + * + * Returns null if the given argument could not be converted to a string. + * + * @function module:core#uc + * + * @param {*} str + * The string to be converted to uppercase. + * + * @returns {string|null} + * + * @example + * uc("hello"); // "HELLO" + * uc(123); // null + */ + static uc_value_t * uc_uc(uc_vm_t *vm, size_t nargs) { @@ -1308,6 +1965,27 @@ uc_uc(uc_vm_t *vm, size_t nargs) return rv; } +/** + * Converts each given numeric value to an UTF-8 multibyte sequence and returns + * the resulting string. + * + * Invalid numeric values or values outside the range `0`..`0x10FFFF` are + * represented by the unicode replacement character `0xFFFD`. + * + * Returns a new UTF-8 encoded string consisting of unicode characters + * corresponding to the given numeric codepoints. + * + * @function module:core#uchr + * + * @param {...number} + * Numeric values to convert. + * + * @returns {string} + * + * @example + * uchr(0x2600, 0x26C6, 0x2601); // "☀⛆☁" + * uchr(-1, 0x20ffff, "foo"); // "���" + */ static uc_value_t * uc_uchr(uc_vm_t *vm, size_t nargs) { @@ -1351,6 +2029,21 @@ uc_uchr(uc_vm_t *vm, size_t nargs) return rv; } +/** + * Returns an array containing all values of the given object. + * + * Returns null if no object was passed. + * + * @function module:core#values + * + * @param {*} obj + * The object from which to extract values. + * + * @returns {Array|null} + * + * @example + * values({ foo: true, bar: false }); // [true, false] + */ static uc_value_t * uc_values(uc_vm_t *vm, size_t nargs) { @@ -1410,18 +2103,77 @@ uc_trim_common(uc_vm_t *vm, size_t nargs, bool start, bool end) return ucv_string_new_length(p, len); } +/** + * 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`, + * `\r`, and `\n`. + * + * Returns the trimmed string. + * + * @function module:core#trim + * + * @param {string} str + * The string to be trimmed. + * + * @param {string} [c] + * The characters to be trimmed from the start and end of the string. + * + * @returns {string} + */ static uc_value_t * uc_trim(uc_vm_t *vm, size_t nargs) { return uc_trim_common(vm, nargs, true, true); } +/** + * Trim any of the specified characters from the start of the string. + * If the second argument is omitted, trims the characters ` ` (space), '\t', + * '\r', and '\n'. + * + * Returns the left trimmed string. + * + * @function module:core#ltrim + * + * @param {string} s + * The input string. + * + * @param {string} [c] + * The characters to trim. + * + * @returns {string} + * + * @example + * ltrim(" foo \n") // "foo \n" + * ltrim("--bar--", "-") // "bar--" + */ static uc_value_t * uc_ltrim(uc_vm_t *vm, size_t nargs) { return uc_trim_common(vm, nargs, true, false); } +/** + * Trim any of the specified characters from the end of the string. + * If the second argument is omitted, trims the characters ` ` (space), '\t', + * '\r', and '\n'. + * +* Returns the right trimmed string. + * + * @function module:core#rtrim + * + * @param {string} str + * The input string. + * + * @param {string} [c] + * The characters to trim. + * + * @returns {string} + * + * @example + * rtrim(" foo \n") // " foo" + * rtrim("--bar--", "-") // "--bar" + */ static uc_value_t * uc_rtrim(uc_vm_t *vm, size_t nargs) { @@ -1725,6 +2477,31 @@ parse_precision: ucv_stringbuf_addstr(buf, last, p - last); } +/** + * Formats the given arguments according to the given format string. + * + * See `printf()` for details. + * + * Returns the formatted string. + * + * @function module:core#sprintf + * + * @param {string} fmt + * The format string. + * + * @param {...*} + * Arguments to be formatted. + * + * @returns {string} + * + * @example + * sprintf("Hello %s", "world"); // "Hello world" + * sprintf("%08x", 123); // "0000007b" + * sprintf("%c%c%c", 65, 98, 99); // "Abc" + * sprintf("%g", 10 / 3.0); // "3.33333" + * sprintf("%2$d %1$d", 12, 34); // "34 12" + * sprintf("%J", [1,2,3]); // "[1,2,3]" + */ static uc_value_t * uc_sprintf(uc_vm_t *vm, size_t nargs) { @@ -1735,6 +2512,60 @@ uc_sprintf(uc_vm_t *vm, size_t nargs) return ucv_stringbuf_finish(buf); } +/** + * Formats the given arguments according to the given format string and outputs + * the result to stdout. + * + * Ucode supports a restricted subset of the formats allowed by the underlying + * libc's `printf()` implementation, namely it allows the `d`, `i`, `o`, `u`, + * `x`, `X`, `e`, `E`, `f`, `F`, `g`, `G`, `c` and `s` conversions. + * + * Additionally, an ucode specific `J` format is implemented, which causes the + * corresponding value to be formatted as JSON string. By prefixing the `J` + * format letter with a precision specifier, the resulting JSON output will be + * pretty printed. A precision of `0` will use tabs for indentation, any other + * positive precision will use that many spaces for indentation while a negative + * or omitted precision specifier will turn off pretty printing. + * + * Other format specifiers such as `n` or `z` are not accepted and returned + * verbatim. Format specifiers including `*` directives are rejected as well. + * + * Returns the number of bytes written to the standard output. + * + * @function module:core#printf + * + * @param {string} fmt + * The format string. + * + * @param {...*} + * Arguments to be formatted. + * + * @returns {number} + * + * @example + * {% + * printf("Hello %s\n", "world"); // Hello world + * printf("%08x\n", 123); // 0000007b + * printf("%c%c%c\n", 65, 98, 99); // Abc + * printf("%g\n", 10 / 3.0); // 3.33333 + * printf("%2$d %1$d\n", 12, 34); // 34 12 + * printf("%J", [1,2,3]); // [ 1, 2, 3 ] + * + * printf("%.J", [1,2,3]); + * // [ + * // 1, + * // 2, + * // 3 + * // ] + * + * printf("%.2J", [1,2,3]); + * // [ + * // 1, + * // 2, + * // 3 + * // ] + * %} + */ static uc_value_t * uc_printf(uc_vm_t *vm, size_t nargs) { @@ -1927,6 +2758,30 @@ uc_require(uc_vm_t *vm, size_t nargs) return uc_require_library(vm, uc_fn_arg(0), false); } +/** + * Convert the given IP address string to an array of byte values. + * + * IPv4 addresses result in arrays of 4 integers while IPv6 ones in arrays + * containing 16 intergers. The resulting array can be turned back into IP + * address strings using the inverse `arrtoip()` function. + * + * Returns an array containing the address byte values. + * Returns `null` if the given argument is not a string or an invalid IP. + * + * @function module:core#iptoarr + * + * @param {string} address + * The IP address string to convert. + * + * @returns {number[]|null} + * + * @example + * iptoarr("192.168.1.1") // [ 192, 168, 1, 1 ] + * iptoarr("fe80::fc54:ff:fe82:abbd") // [ 254, 128, 0, 0, 0, 0, 0, 0, 252, 84, + * // 0, 255, 254, 130, 171, 189 ]) + * iptoarr("foo") // null (invalid address) + * iptoarr(123) // null (not a string) + */ static uc_value_t * uc_iptoarr(uc_vm_t *vm, size_t nargs) { @@ -1980,6 +2835,31 @@ check_byte(uc_value_t *v) return n; } +/** + * Convert the given input array of byte values to an IP address string. + * + * Input arrays of length 4 are converted to IPv4 addresses, arrays of length 16 + * to IPv6 ones. All other lengths are rejected. If any array element is not an + * integer or exceeds the range 0..255 (inclusive), the array is rejected. + * + * Returns a string containing the formatted IP address. + * Returns `null` if the input array was invalid. + * + * @function module:core#arrtoip + * + * @param {number[]} arr + * The byte array to convert into an IP address string. + * + * @returns {string|null} + * + * @example + * arrtoip([ 192, 168, 1, 1 ]) // "192.168.1.1" + * arrtoip([ 254, 128, 0, 0, 0, 0, 0, 0, 252, 84, 0, 255, 254, 130, 171, 189 ]) + * // "fe80::fc54:ff:fe82:abbd" + * arrtoip([ 1, 2, 3]) // null (invalid length) + * arrtoip([ 1, "2", -5, 300 ]) // null (invalid values) + * arrtoip("123") // null (not an array) + */ static uc_value_t * uc_arrtoip(uc_vm_t *vm, size_t nargs) { @@ -2028,6 +2908,31 @@ uc_arrtoip(uc_vm_t *vm, size_t nargs) } } +/** + * Match the given string against the regular expression pattern specified as + * the second argument. + * + * If the passed regular expression uses the `g` flag, the return value will be + * an array of arrays describing all found occurrences within the string. + * + * Without the `g` modifier, an array describing the first match is returned. + * + * Returns `null` if the pattern was not found within the given string. + * + * @function module:core#match + * + * @param {string} str + * The string to be matched against the pattern. + * + * @param {RegExp} pattern + * The regular expression pattern. + * + * @returns {Array|null} + * + * @example + * match("foobarbaz", /b.(.)/) // ["bar", "r"] + * match("foobarbaz", /b.(.)/g) // [["bar", "r"], ["baz", "z"]] + */ static uc_value_t * uc_match(uc_vm_t *vm, size_t nargs) { @@ -2202,6 +3107,54 @@ uc_replace_str(uc_vm_t *vm, uc_value_t *str, free(r); } +/** + * Replace occurrences of the specified pattern in the string passed as the + * first argument. + * + * - The pattern value may be either a regular expression or a plain string. + * - The replace value may be a function which is invoked for each found pattern + * or any other value which is converted into a plain string and used as + * replacement. + * - When an optional limit is specified, substitutions are performed only that + * many times. + * - If the pattern is a regular expression and not using the `g` flag, then + * only the first occurrence in the string is replaced. + * - If the `g` flag is used or if the pattern is not a regular expression, all + * occurrences are replaced. + * - If the replace value is a callback function, it is invoked with the found + * substring as the first and any capture group values as subsequent + * parameters. + * - If the replace value is a string, specific substrings are substituted + * before it is inserted into the result. + * + * Returns a new string with the pattern replaced. + * + * @function module:core#replace + * + * @param {string} str + * The string in which to replace occurrences. + * + * @param {RegExp|string} pattern + * The pattern to be replaced. + * + * @param {Function|string} replace + * The replacement value. + * + * @param {number} [limit] + * The optional limit of substitutions. + * + * @returns {string} + * + * @example + * replace("barfoobaz", /(f)(o+)/g, "[$$|$`|$&|$'|$1|$2|$3]") // bar[$|bar|foo|baz|f|oo|$3]baz + * replace("barfoobaz", /(f)(o+)/g, uc) // barFOObaz + * replace("barfoobaz", "a", "X") // bXrfoobXz + * replace("barfoobaz", /(.)(.)(.)/g, function(m, c1, c2, c3) { + * return c3 + c2 + c1; + * }) // raboofzab + * replace("aaaaa", "a", "x", 3) // xxxaa + * replace("foo bar baz", /[ao]/g, "x", 3) // fxx bxr baz + */ static uc_value_t * uc_replace(uc_vm_t *vm, size_t nargs) { @@ -2435,6 +3388,44 @@ uc_json_from_string(uc_vm_t *vm, uc_value_t *str, json_object **jso) return tok; } +/** + * Parse the given string or resource as JSON and return the resulting value. + * + * If the input argument is a plain string, it is directly parsed as JSON. + * + * If an array, object or resource value is given, this function will attempt to + * invoke a `read()` method on it to read chunks of input text to incrementally + * parse as JSON data. Reading will stop if the object's `read()` method returns + * either `null` or an empty string. + * + * Throws an exception on parse errors, trailing garbage, or premature EOF. + * + * Returns the parsed JSON data. + * + * @function module:core#json + * + * @param {string} str_or_resource + * The string or resource object to be parsed as JSON. + * + * @returns {*} + * + * @example + * json('{"a":true, "b":123}') // { "a": true, "b": 123 } + * json('[1,2,') // Throws an exception + * + * import { open } from 'fs'; + * let fd = open('example.json', 'r'); + * json(fd); // will keep invoking `fd.read()` until EOF and + * // incrementally parse each read chunk. + * + * let x = proto( + * [ '{"foo":', 'true, ', '"bar":', 'false}' ], + * { read: function() { return shift(this) } } + * ); + * json(x); // will keep invoking `x.read()` until array + * // is empty incrementally parse each piece + * + */ static uc_value_t * uc_json(uc_vm_t *vm, size_t nargs) { @@ -2583,12 +3574,92 @@ uc_include_common(uc_vm_t *vm, size_t nargs, bool raw_mode) return NULL; } +/** + * Evaluate and include the file at the given path and optionally override the + * execution scope with the given scope object. + * + * By default, the file is executed within the same scope as the calling + * `include()`, but by passing an object as the second argument, it is possible + * to extend the scope available to the included file. + * + * This is useful to supply additional properties as global variables to the + * included code. To sandbox included code, that is giving it only access to + * explicitly provided properties, the `proto()` function can be used to create + * a scope object with an empty prototype. + * + * @function module:core#include + * + * @param {string} path + * The path to the file to be included. + * + * @param {Object} [scope] + * The optional scope object to override the execution scope. + * + * @example + * // Load and execute "foo.uc" immediately + * include("./foo.uc") + * + * // Execute the "supplemental.ucode" in an extended scope and make the "foo" + * // and "bar" properties available as global variables + * include("./supplemental.uc", { + * foo: true, + * bar: 123 + * }) + * + * // Execute the "untrusted.ucode" in a sandboxed scope and make the "foo" and + * // "bar" variables as well as the "print" function available to it. + * // By assigning an empty prototype object to the scope, included code has no + * // access to other global values anymore. + * include("./untrusted.uc", proto({ + * foo: true, + * bar: 123, + * print: print + * }, {})) + */ static uc_value_t * uc_include(uc_vm_t *vm, size_t nargs) { return uc_include_common(vm, nargs, vm->config && vm->config->raw_mode); } +/** + * When invoked with a string value as the first argument, the function acts + * like `include()` but captures the output of the included file as a string and + * returns the captured contents. + * + * The second argument is treated as the scope. + * + * When invoked with a function value as the first argument, `render()` calls + * the given function and passes all subsequent arguments to it. + * + * Any output produced by the called function is captured and returned as a + * string. The return value of the called function is discarded. + * + * @function module:core#render + * + * @param {string|Function} path_or_func + * The path to the file or the function to be rendered. + * + * @param {Object|*} [scope_or_fnarg1] + * The optional scope or the first argument for the function. + * + * @param {*} [fnarg2] + * The second argument for the function. + * + * @param {...*} [fnargN] + * Additional arguments for the function. + * + * @returns {string} + * + * @example + * // Renders template file with given scope and captures the output as a string + * const output = render("./template.uc", { foo: "bar" }); + * + * // Calls a function, captures the output, and returns it as a string + * const result = render(function(name) { + * printf("Hello, %s!\n", name); + * }, "Alice"); + */ static uc_value_t * uc_render(uc_vm_t *vm, size_t nargs) { @@ -2640,6 +3711,23 @@ out: return NULL; } +/** + * Print any of the given values to stderr. Arrays and objects are converted to + * their JSON representation. + * + * Returns the amount of bytes printed. + * + * @function module:core#warn + * + * @param {...*} x + * The values to be printed. + * + * @returns {number} + * + * @example + * warn("Hello", "world"); // Print "Helloworld" to stderr + * warn({ key: "value" }); // Print JSON representation of the object to stderr + */ static uc_value_t * uc_warn(uc_vm_t *vm, size_t nargs) { @@ -2747,6 +3835,49 @@ sigtimedwait(const sigset_t *set, siginfo_t *info, const struct timespec *timeou #endif +/** + * Executes the given command, waits for completion, and returns the resulting + * exit code. + * + * The command argument may be either a string, in which case it is passed to + * `/bin/sh -c`, or an array, which is directly converted into an `execv()` + * argument vector. + * + * - If the program terminated normally, a positive integer holding the + * program's `exit()` code is returned. + * - If the program was terminated by an uncaught signal, a negative signal + * number is returned. + * - If the optional timeout argument is specified, the program is terminated + * by `SIGKILL` after that many milliseconds if it doesn't complete within + * the timeout. + * + * Omitting the timeout argument or passing `0` disables the command timeout. + * + * Returns the program exit code. + * + * @function module:core#system + * + * @param {string|Array} command + * The command to be executed. + * + * @param {number} [timeout] + * The optional timeout in milliseconds. + * + * @returns {number} + * + * @example + * // Execute through `/bin/sh` + * // prints "Hello world" to stdout and returns 3 + * system("echo 'Hello world' && exit 3"); + * + * // Execute argument vector + * // prints the UNIX timestamp to stdout and returns 0 + * system(["/usr/bin/date", "+%s"]); + * + * // Apply a timeout + * // returns -9 + * system("sleep 3 && echo 'Success'", 1000); + */ static uc_value_t * uc_system(uc_vm_t *vm, size_t nargs) { @@ -2890,6 +4021,23 @@ fail: return NULL; } +/** + * 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 as the program is executed. + * + * Invoking `trace()` with zero as an argument turns off opcode tracing. + * + * @function module:core#trace + * + * @param {number} level + * The level of tracing to enable. + * + * @example + * trace(1); // Enables opcode tracing + * trace(0); // Disables opcode tracing + */ static uc_value_t * uc_trace(uc_vm_t *vm, size_t nargs) { @@ -2908,6 +4056,33 @@ uc_trace(uc_vm_t *vm, size_t nargs) return ucv_int64_new(prev_level); } +/** + * Get or set the prototype of the array or object value `val`. + * + * When invoked without a second argument, the function returns the current + * prototype of the value in `val` or `null` if there is no prototype or if the + * given value is neither an object nor an array. + * + * When invoked with a second prototype argument, the given `proto` value is set + * as the prototype on the array or object in `val`. + * + * Throws an exception if the given prototype value is not an object. + * + * @function module:core#proto + * + * @param {Array|Object} val + * The array or object value. + * + * @param {Object} [proto] + * The optional prototype object. + * + * @returns {Object|null} + * + * @example + * const arr = [1, 2, 3]; + * proto(arr); // Returns the current prototype of the array (null by default) + * proto(arr, { foo: true }); // Sets the given object as the prototype of the array + */ static uc_value_t * uc_proto(uc_vm_t *vm, size_t nargs) { @@ -2927,6 +4102,19 @@ uc_proto(uc_vm_t *vm, size_t nargs) return ucv_get(val); } +/** + * Pause execution for the given amount of milliseconds. + * + * @function module:core#sleep + * + * @param {number} milliseconds + * The amount of milliseconds to sleep. + * + * @returns {boolean} + * + * @example + * sleep(1000); // Sleeps for 1 second + */ static uc_value_t * uc_sleep(uc_vm_t *vm, size_t nargs) { @@ -2947,6 +4135,26 @@ uc_sleep(uc_vm_t *vm, size_t nargs) return ucv_boolean_new(true); } +/** + * Raise an exception with the given message parameter when the value in `cond` + * is not truish. + * + * When `message` is omitted, the default value is `Assertion failed`. + * + * @function module:core#assert + * + * @param {*} cond + * The value to check for truthiness. + * + * @param {string} [message] + * The message to include in the exception. + * + * @throws {Error} When the condition is falsy. + * + * @example + * assert(true, "This is true"); // No exception is raised + * assert(false); // Exception is raised with the default message "Assertion failed" + */ static uc_value_t * uc_assert(uc_vm_t *vm, size_t nargs) { @@ -2969,6 +4177,32 @@ uc_assert(uc_vm_t *vm, size_t nargs) return ucv_get(cond); } +/** + * Construct a regular expression instance from the given `source` pattern + * string and any flags optionally specified by the `flags` argument. + * + * - Throws a type error exception if `flags` is not a string or if the string + * in `flags` contains unrecognized regular expression flag characters. + * - Throws a syntax error when the pattern in `source` cannot be compiled into + * a valid regular expression. + * + * Returns the compiled regular expression value. + * + * @function module:core#regexp + * + * @param {string} source + * The pattern string. + * + * @param {string} [flags] + * The optional regular expression flags. + * + * @returns {RegExp} + * + * @example + * regexp('foo.*bar', 'is'); // equivalent to /foo.*bar/is + * regexp('foo.*bar', 'x'); // throws a "Type error: Unrecognized flag character 'x'" exception + * regexp('foo.*('); // throws a "Syntax error: Unmatched ( or \( exception" + */ static uc_value_t * uc_regexp(uc_vm_t *vm, size_t nargs) { @@ -3024,6 +4258,34 @@ uc_regexp(uc_vm_t *vm, size_t nargs) return regex; } +/** + * Match the given subject against the supplied wildcard (file glob) pattern. + * + * - If a truthy value is supplied as the third argument, case-insensitive + * matching is performed. + * - If a non-string value is supplied as the subject, it is converted into a + * string before being matched. + * + * Returns `true` when the value matched the given pattern, otherwise `false`. + * + * @function module:core#wildcard + * + * @param {*} subject + * The subject to match against the wildcard pattern. + * + * @param {string} pattern + * The wildcard pattern. + * + * @param {boolean} [nocase] + * Whether to perform case-insensitive matching. + * + * @returns {boolean} + * + * @example + * wildcard("file.txt", "*.txt"); // Returns true + * wildcard("file.txt", "*.TXT", true); // Returns true (case-insensitive match) + * wildcard("file.txt", "*.jpg"); // Returns false + */ static uc_value_t * uc_wildcard(uc_vm_t *vm, size_t nargs) { @@ -3049,6 +4311,24 @@ uc_wildcard(uc_vm_t *vm, size_t nargs) return ucv_boolean_new(rv == 0); } +/** + * Determine the path of the source file currently being executed by ucode. + * + * @function module:core#sourcepath + * + * @param {number} [depth=0] + * The depth to walk up the call stack. + * + * @param {boolean} [dironly] + * Whether to return only the directory portion of the source file path. + * + * @returns {string|null} + * + * @example + * sourcepath(); // Returns the path of the currently executed file + * sourcepath(1); // Returns the path of the parent source file + * sourcepath(2, true); // Returns the directory portion of the grandparent source file path + */ static uc_value_t * uc_sourcepath(uc_vm_t *vm, size_t nargs) { @@ -3111,12 +4391,46 @@ uc_min_max(uc_vm_t *vm, size_t nargs, int cmp) return ucv_get(rv); } +/** + * Return the smallest value among all parameters passed to the function. + * + * @function module:core#min + * + * @param {...*} [val] + * The values to compare. + * + * @returns {*} + * + * @example + * min(5, 2.1, 3, "abc", 0.3); // Returns 0.3 + * min(1, "abc"); // Returns 1 + * min("1", "abc"); // Returns "1" + * min("def", "abc", "ghi"); // Returns "abc" + * min(true, false); // Returns false + */ static uc_value_t * uc_min(uc_vm_t *vm, size_t nargs) { return uc_min_max(vm, nargs, I_LT); } +/** + * Return the largest value among all parameters passed to the function. + * + * @function module:core#max + * + * @param {...*} [val] + * The values to compare. + * + * @returns {*} + * + * @example + * max(5, 2.1, 3, "abc", 0.3); // Returns 5 + * max(1, "abc"); // Returns 1 (!) + * max("1", "abc"); // Returns "abc" + * max("def", "abc", "ghi"); // Returns "ghi" + * max(true, false); // Returns true + */ static uc_value_t * uc_max(uc_vm_t *vm, size_t nargs) { @@ -3200,6 +4514,25 @@ uc_max(uc_vm_t *vm, size_t nargs) it returns the number of data bytes stored at the target, or -1 on error. */ +/** + * Decodes the given base64 encoded string and returns the decoded result. + * + * - If non-whitespace, non-base64 characters are encountered, if invalid + * padding or trailing garbage is found, the function returns `null`. + * - If a non-string argument is given, the function returns `null`. + * + * @function module:core#b64dec + * + * @param {string} str + * The base64 encoded string to decode. + * + * @returns {string|null} + * + * @example + * b64dec("VGhpcyBpcyBhIHRlc3Q="); // Returns "This is a test" + * b64dec(123); // Returns null + * b64dec("XXX"); // Returns null + */ static uc_value_t * uc_b64dec(uc_vm_t *vm, size_t nargs) { @@ -3333,6 +4666,22 @@ err: static const char Base64[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; +/** + * Encodes the given string into base64 and returns the resulting string. + * + * - If a non-string argument is given, the function returns `null`. + * + * @function module:core#b64enc + * + * @param {string} str + * The string to encode. + * + * @returns {string|null} + * + * @example + * b64enc("This is a test"); // Returns "VGhpcyBpcyBhIHRlc3Q=" + * b64enc(123); // Returns null + */ static uc_value_t * uc_b64enc(uc_vm_t *vm, size_t nargs) { @@ -3453,6 +4802,23 @@ uc_uniq_ucv_equal(const void *k1, const void *k2) return ucv_is_equal(uv1, uv2); } +/** + * Returns a new array containing all unique values of the given input array. + * + * - The order is preserved, and subsequent duplicate values are skipped. + * - If a non-array argument is given, the function returns `null`. + * + * @function module:core#uniq + * + * @param {Array} array + * The input array. + * + * @returns {Array|null} + * + * @example + * uniq([1, true, "foo", 2, true, "bar", "foo"]); // Returns [1, true, "foo", 2, "bar"] + * uniq("test"); // Returns null + */ static uc_value_t * uc_uniq(uc_vm_t *vm, size_t nargs) { @@ -3511,12 +4877,81 @@ uc_gettime_common(uc_vm_t *vm, size_t nargs, bool local) return res; } +/** + * Return the given epoch timestamp (or now, if omitted) as a dictionary + * containing broken-down date and time information according to the local + * system timezone. + * + * The resulting dictionary contains the following fields: + * - `sec` Seconds (0-60) + * - `min` Minutes (0-59) + * - `hour` Hours (0-23) + * - `mday` Day of month (1-31) + * - `mon` Month (1-12) + * - `year` Year (>= 1900) + * - `wday` Day of the week (1-7, Sunday = 7) + * - `yday` Day of the year (1-366, Jan 1st = 1) + * - `isdst` Daylight saving time in effect (yes = 1) + * + * Note that in contrast to the underlying `localtime(3)` C library function, + * the values for `mon`, `wday`, and `yday` are 1-based, and the `year` is + * 1900-based. + * + * @function module:core#localtime + * + * @param {number} [epoch] + * The epoch timestamp. + * + * @returns {Object} + * + * @example + * localtime(1647953502); + * // Returns: + * // { + * // sec: 42, + * // min: 51, + * // hour: 13, + * // mday: 22, + * // mon: 3, + * // year: 2022, + * // wday: 2, + * // yday: 81, + * // isdst: 0 + * // } + */ static uc_value_t * uc_localtime(uc_vm_t *vm, size_t nargs) { return uc_gettime_common(vm, nargs, true); } +/** + * Like `localtime()` but interpreting the given epoch value as UTC time. + * + * See `localtime()` for details on the return value. + * + * @function module:core#gmtime + * + * @param {number} [epoch] + * The epoch timestamp. + * + * @returns {Object} + * + * @example + * gmtime(1647953502); + * // Returns: + * // { + * // sec: 42, + * // min: 51, + * // hour: 13, + * // mday: 22, + * // mon: 3, + * // year: 2022, + * // wday: 2, + * // yday: 81, + * // isdst: 0 + * // } + */ static uc_value_t * uc_gmtime(uc_vm_t *vm, size_t nargs) { @@ -3572,18 +5007,85 @@ uc_mktime_common(uc_vm_t *vm, size_t nargs, bool local) return (t != (time_t)-1) ? ucv_int64_new((int64_t)t) : NULL; } +/** + * Performs the inverse operation of `localtime()` by taking a broken-down date + * and time dictionary and transforming it into an epoch value according to the + * local system timezone. + * + * The `wday` and `yday` fields of the given date time specification are + * ignored. Field values outside of their valid range are internally normalized, + * e.g. October 40th is interpreted as November 9th. + * + * Returns the resulting epoch value or null if the input date time dictionary + * was invalid or if the date time specification cannot be represented as epoch + * value. + * + * @function module:core#timelocal + * + * @param {Object} datetimespec + * The broken-down date and time dictionary. + * + * @returns {number|null} + * + * @example + * timelocal({ "sec": 42, "min": 51, "hour": 13, "mday": 22, "mon": 3, "year": 2022, "isdst": 0 }); + * // Returns 1647953502 + */ static uc_value_t * uc_timelocal(uc_vm_t *vm, size_t nargs) { return uc_mktime_common(vm, nargs, true); } +/** + * Like `timelocal()` but interpreting the given date time specification as UTC + * time. + * + * See `timelocal()` for details. + * + * @function module:core#timegm + * + * @param {Object} datetimespec + * The broken-down date and time dictionary. + * + * @returns {number|null} + * + * @example + * timegm({ "sec": 42, "min": 51, "hour": 13, "mday": 22, "mon": 3, "year": 2022, "isdst": 0 }); + * // Returns 1647953502 + */ static uc_value_t * uc_timegm(uc_vm_t *vm, size_t nargs) { return uc_mktime_common(vm, nargs, false); } +/** + * Reads the current second and microsecond value of the system clock. + * + * By default, the realtime clock is queried which might skew forwards or + * backwards due to NTP changes, system sleep modes etc. If a truish value is + * passed as argument, the monotonic system clock is queried instead, which will + * return the monotonically increasing time since some arbitrary point in the + * past (usually the system boot time). + * + * Returns a two element array containing the full seconds as the first element + * and the nanosecond fraction as the second element. + * + * Returns `null` if a monotonic clock value is requested and the system does + * not implement this clock type. + * + * @function module:core#clock + * + * @param {boolean} [monotonic] + * Whether to query the monotonic system clock. + * + * @returns {Array<number>|null} + * + * @example + * clock(); // [ 1647954926, 798269464 ] + * clock(true); // [ 474751, 527959975 ] + */ static uc_value_t * uc_clock(uc_vm_t *vm, size_t nargs) { @@ -3602,6 +5104,20 @@ uc_clock(uc_vm_t *vm, size_t nargs) return res; } +/** + * Encodes the given byte string into a hexadecimal digit string, converting + * the input value to a string if needed. + * + * @function module:core#hexenc + * + * @param {string} val + * The byte string to encode. + * + * @returns {string} + * + * @example + * hexenc("Hello world!\n"); // "48656c6c6f20776f726c64210a" + */ static uc_value_t * uc_hexenc(uc_vm_t *vm, size_t nargs) { @@ -3643,6 +5159,31 @@ hexval(unsigned char c, bool lo) return ((c > '9') ? (c - 'a') + 10 : c - '0') << (lo ? 0 : 4); } +/** + * Decodes the given hexadecimal digit string into a byte string, optionally + * skipping specified characters. + * + * If the characters to skip are not specified, a default of `" \t\n"` is used. + * + * Returns null if the input string contains invalid characters or an uneven + * amount of hex digits. + * + * Returns the decoded byte string on success. + * + * @function module:core#hexdec + * + * @param {string} hexstring + * The hexadecimal digit string to decode. + * + * @param {string} [skipchars] + * The characters to skip during decoding. + * + * @returns {string|null} + * + * @example + * hexdec("48656c6c6f20776f726c64210a"); // "Hello world!\n" + * hexdec("44:55:66:77:33:44", ":"); // "DUfw3D" + */ static uc_value_t * uc_hexdec(uc_vm_t *vm, size_t nargs) { @@ -3694,6 +5235,43 @@ uc_hexdec(uc_vm_t *vm, size_t nargs) return ucv_stringbuf_finish(buf); } +/** + * Interacts with the mark and sweep garbage collector of the running ucode + * virtual machine. + * + * Depending on the given `operation` string argument, the meaning of `argument` + * and the function return value differs. + * + * The following operations are defined: + * + * - `collect` - Perform a complete garbage collection cycle, returns `true`. + * - `start` - (Re-)start periodic garbage collection, `argument` is an optional + * integer in the range `1..65535` specifying the interval. + * Defaults to `1000` if omitted. Returns `true` if the periodic GC + * was previously stopped and is now started or if the interval + * changed. Returns `false` otherwise. + * - `stop` - Stop periodic garbage collection. Returns `true` if the periodic + * GC was previously started and is now stopped, `false` otherwise. + * - `count` - Count the amount of active complex object references in the VM + * context, returns the counted amount. + * + * If the `operation` argument is omitted, the default is `collect`. + * + * @function module:core#gc + * + * @param {string} [operation] + * The operation to perform. + * + * @param {*} [argument] + * The argument for the operation. + * + * @returns {boolean|number|null} + * + * @example + * gc(); // true + * gc("start"); // true + * gc("count"); // 42 + */ static uc_value_t * uc_gc(uc_vm_t *vm, size_t nargs) { @@ -3820,6 +5398,55 @@ uc_load_common(uc_vm_t *vm, size_t nargs, uc_source_t *source) return closure; } +/** + * Compiles the given code string into a ucode program and returns the resulting + * program entry function. + * + * The optional `options` dictionary overrides parse and compile options. + * + * - If a non-string `code` argument is given, it is implicitly converted to a + * string value first. + * - If `options` is omitted or a non-object value, the compile options of the + * running ucode program are reused. + * + * The following keys in the `options` dictionary are recognized: + * + * | Key | Type | Description | + * |-----------------------|-------|----------------------------------------------------------| + * | `lstrip_blocks` | bool | Strip leading whitespace before statement template blocks| + * | `trim_blocks` | bool | Strip newline after statement template blocks | + * | `strict_declarations` | bool | Treat access to undefined variables as fatal error | + * | `raw_mode` | bool | Compile source in script mode, don't treat it as template| + * | `module_search_path` | array | Override compile time module search path | + * | `force_dynlink_list` | array | List of module names to treat as dynamic extensions | + * + * Unrecognized keys are ignored, unspecified options default to those of the + * running program. + * + * Returns the compiled program entry function. + * + * Throws an exception on compilation errors. + * + * @function module:core#loadstring + * + * @param {string} code + * The code string to compile. + * + * @param {Object} [options] + * The options for compilation. + * + * @returns {Function} + * + * @example + * let fn1 = loadstring("Hello, {{ name }}", { raw_mode: false }); + * + * global.name = "Alice"; + * fn1(); // prints `Hello, Alice` + * + * + * let fn2 = loadstring("return 1 + 2;", { raw_mode: true }); + * fn2(); // 3 + */ static uc_value_t * uc_loadstring(uc_vm_t *vm, size_t nargs) { @@ -3851,6 +5478,30 @@ uc_loadstring(uc_vm_t *vm, size_t nargs) return uc_load_common(vm, nargs, source); } +/** + * Compiles the given file into a ucode program and returns the resulting + * program entry function. + * + * See `loadstring()` for details. + * + * Returns the compiled program entry function. + * + * Throws an exception on compilation or file I/O errors. + * + * @function module:core#loadfile + * + * @param {string} path + * The path of the file to compile. + * + * @param {Object} [options] + * The options for compilation. + * + * @returns {Function} + * + * @example + * loadfile("./templates/example.uc"); // function main() { ... } + */ + static uc_value_t * uc_loadfile(uc_vm_t *vm, size_t nargs) { @@ -3873,6 +5524,76 @@ uc_loadfile(uc_vm_t *vm, size_t nargs) return uc_load_common(vm, nargs, source); } +/** + * 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 `scope` value as global environment. Any further arguments are + * passed to the invoked function as-is. + * + * When `ctx` is omitted or `null`, the function will get invoked with `this` + * being `null`. + * + * When `scope` is omitted or `null`, the function will get executed with the + * current global environment of the running program. When `scope` is set to a + * dictionary, the dictionary is used as global function environment. + * + * When the `scope` dictionary has no prototype, the current global environment + * will be set as prototype, means the scope will inherit from it. + * + * When a scope prototype is set, it is kept. This allows passing an isolated + * (sandboxed) function scope without access to the global environment. + * + * Any further argument is forwarded as-is to the invoked function as function + * call argument. + * + * Returns `null` if the given function value `fn` is not callable. + * + * Returns the return value of the invoked function in all other cases. + * + * Forwards exceptions thrown by the invoked function. + * + * @function module:core#call + * + * @param {Function} fn + * Function value to call. + * + * @param {*} [ctx=null] + * `this` context for the invoked function. + * + * @param {Object} [scope=null] + * Global environment for the invoked function. + * + * @param {...*} [arg] + * Additional arguments to pass to the invoked function. + * + * @returns {*} + * + * @example + * // Override this context + * call(function() { printf("%J\n", this) }); // null + * call(function() { printf("%J\n", this) }, null); // null + * call(function() { printf("%J\n", this) }, { x: 1 }); // { "x": 1 } + * call(function() { printf("%J\n", this) }, { x: 2 }); // { "x": 2 } + * + * // Run with default scope + * global.a = 1; + * call(function() { printf("%J\n", a) }); // 1 + * + * // Override scope, inherit from current global scope (implicit) + * call(function() { printf("%J\n", a) }, null, { a: 2 }); // 2 + * + * // Override scope, inherit from current global scope (explicit) + * call(function() { printf("%J\n", a) }, null, + * proto({ a: 2 }, global)); // 2 + * + * // Override scope, don't inherit (pass `printf()` but not `a`) + * call(function() { printf("%J\n", a) }, null, + * proto({}, { printf })); // null + * + * // Forward arguments + * x = call((x, y, z) => x * y * z, null, null, 2, 3, 4); // x = 24 + */ static uc_value_t * uc_callfunc(uc_vm_t *vm, size_t nargs) { |