diff options
author | Jo-Philipp Wich <jo@mein.io> | 2023-07-12 16:45:12 +0200 |
---|---|---|
committer | Jo-Philipp Wich <jo@mein.io> | 2023-07-13 10:41:24 +0200 |
commit | f1190ef2f2719582efcad4109f1215b02baebfdf (patch) | |
tree | d9a2644de3ba6c1032a4a3b5643fa78b26a84aa8 /README.md | |
parent | cba0c3cf5312cdaed0538bb3dbf87b31b3e89916 (diff) |
docs: various improvements
- Switch JSDoc theme to "clean-jsdoc-theme"
- Add some custom CSS and JS tweaks to the theme
- Use a condensed README.md for the toplevel directory
- Include a longer README.md in the documentation portal
- Tweak JSDoc annotations for better output results
- Register `ucode.mein.io` CNAME
Signed-off-by: Jo-Philipp Wich <jo@mein.io>
Diffstat (limited to 'README.md')
-rw-r--r-- | README.md | 1760 |
1 files changed, 45 insertions, 1715 deletions
@@ -1,1730 +1,60 @@ -# ABOUT +# The ucode Scripting Language -The ucode language is a tiny general purpose scripting language featuring a -syntax closely resembling ECMAScript. It can be used in a stand-alone manner -by using the ucode command line interpreter or embedded into host applications -by linking libucode and utilizing its C language API. Additionally, ucode can -be invoked in template mode where control flow and expression logic statements -are embedded in Jinja-like markup blocks. +The ucode language is a small, general-purpose scripting language that resembles +ECMAScript syntax. It can be used as a standalone interpreter or embedded into +host applications. Ucode supports template mode with control flow and expression +logic statements embedded in Jinja-like markup blocks. -Besides aiming for small size, the major design goals of ucode are the ability -to trivially read and write JSON data, good embeddability into C applications, -template capabilities for output formatting, extensiblity through loadable -native extension modules and a straightforward set of built-in functions -mimicking those found in the Perl 5 language. +The development of ucode was motivated by the need to rewrite the OpenWrt +firewall framework using nftables. Initially intended as a template processor, +ucode evolved into a versatile scripting language for various system scripting +tasks. Its design goals include easy integration with C applications, efficient +handling of JSON data and complex data structures, support for OpenWrt's ubus +message bus system, and a comprehensive set of built-in functions inspired by +Perl 5. -## HISTORY AND MOTIVATION +Ucode provides the ability to embed code logic into plain text templates, +supports JSON parsing and serialization, has distinct array and object types, +includes built-in support for bit operations and regular expressions, and offers +bindings for relevant Linux and OpenWrt APIs. It follows ECMAScript syntax for +familiarity and reusability of existing tooling, emphasizes synchronous +programming flow, aims for a small executable size, and can be embedded into C +host applications. -In spring 2021 it has been decided to rewrite the OpenWrt firewall framework on -top of nftables with the goal to replace the then current C application with a -kind of preprocessor generating nftables rulesets using a set of templates -instead of relying on built-in hardcoded rules like its predecessor. +In summary, ucode is a synchronous scripting language resembling ECMAScript, +designed for template processing, system scripting tasks, and integration into C +applications, with features such as JSON support, comprehensive built-in +functions, and bindings for relevant APIs. -That decision spurred the development of *ucode*, initially meant to be a -simple template processor solely for the OpenWrt nftables firewall but quickly -evolving into a general purpose scripting language suitable for a wider range -of system scripting tasks. +## Installation -Despite OpenWrt predominantly relying on POSIX shell and Lua as system -scripting languages already, a new solution was needed to accomodate the needs -of the new firewall implementation; mainly the ability to efficiently deal with -JSON data and complex data structures such as arrays and dictionaries and the -ability to closely interface with OpenWrt's *ubus* message bus system. +The *ucode* package should be already preinstalled on modern OpenWrt releases. -Throughout the design process of the new firewall and its template processor, -the following design goals were defined for the *ucode* scripting language: +To learn how to install it on other systems, refer to the +[Installation Section](https://ucode.mein.io/#installation) in the +documentation. - - Ability to embed code logic fragments such as control flow statements, - function calls or arithmetic expressions into plain text templates, using - a block syntax and functionality roughly inspired by Jinja templates - - Built-in support for JSON data parsing and serialization, without the need - for external libraries - - Distinct array and object types (compared to Lua's single table datatype) - - Distinct integer and float types and guaranteed 64bit integer range - - Built-in support for bit operations - - Built-in support for (POSIX) regular expressions - - A comprehensive set of built-in standard functions, inspired by the core - functions found in the Perl 5 interpreter - - Staying as close to ECMAScript syntax as possible due to higher developer - familiarity and to be able to reuse existing tooling such as editor syntax - highlighting - - Bindings for all relevant Linux and OpenWrt APIs, such as *ubus*, *uci*, - *uloop*, *netlink* etc. - - Procedural, synchronous programming flow - - Very small executable size (the interpreter and runtime is currently around - 64KB on ARM Cortex A9) - - Embeddability into C host applications +## Documentation -Summarized, *ucode* can be described as synchronous ECMAScript without the -object oriented standard library. +The most up-to-date documentation is hosted at the +[ucode documentation portal](https://ucode.mein.io/). +You can build the documentation yourself by running `npm install` followed by +`npm run doc` in the cloned repository. The generated documentation will be +placed in the `docs/` directory. -# INSTALLATION +## Examples -## OpenWrt +Examples for embedding ucode into C applications can be found in the +[`examples/` directory](https://github.com/jow-/ucode/tree/master/examples). -In OpenWrt 22.03 and later, *ucode* should already be preinstalled. If not, -it can be installed via the package manager, using the `opkg install ucode` -command. +Notable OpenWrt programs *embedding* ucode are the +[OpenWrt ubus rpc daemon](https://github.com/openwrt/rpcd) and the +[Tiny uhttpd web server](https://github.com/openwrt/uhttpd). -## MacOS +Some ucode scripting examples can be found in the ucode +[testcase sources](https://github.com/jow-/ucode/tree/master/tests/custom). -To build on MacOS, first install *cmake* and *json-c* via -[Homebrew](https://brew.sh/), then clone the ucode repository and execute -*cmake* followed by *make*: - - $ brew install cmake json-c - $ git clone https://github.com/jow-/ucode.git - $ cd ucode/ - $ cmake -DUBUS_SUPPORT=OFF -DUCI_SUPPORT=OFF -DULOOP_SUPPORT=OFF . - $ make - $ sudo make install - -## Debian - -The ucode repository contains build recipes for Debian packages, to build .deb -packages for local installation, first install required development packages, -then clone the repository and invoke *dpkg-buildpackage* to produce the binary -package files: - - $ sudo apt-get install build-essential devscripts debhelper libjson-c-dev cmake pkg-config - $ git clone https://github.com/jow-/ucode.git - $ cd ucode/ - $ dpkg-buildpackage -b -us -uc - $ sudo dpkg -i ../ucode*.deb ../libucode*.deb - -## Other Linux systems - -To install ucode from source on other systems, ensure that the json-c library -and associated development headers are installed, then clone and compile the -ucode repository: - - $ git clone https://github.com/jow-/ucode.git - $ cd ucode/ - $ cmake -DUBUS_SUPPORT=OFF -DUCI_SUPPORT=OFF -DULOOP_SUPPORT=OFF . - $ make - $ sudo make install - - -# SYNTAX - -## Template mode - -By default, *ucode* is executed in *raw mode*, means it expects a given source -file to only contain script code. By invoking the ucode interpreter with the -`-T` flag or by using the `utpl` alias, the *ucode* interpreter is switched -into *template mode* where the source file is expected to be a plaintext file -containing *template blocks* containing ucode script expressions or comments. - -### Block types - -There are three kinds of blocks; *expression blocks*, *statement blocks* and -*comment blocks*. The former two embed code logic using ucode's JavaScript-like -syntax while the latter comment block type is simply discarded during -processing. - - -#### 1. Statement block - -Statement blocks are enclosed in an opening `{%` and a closing `%}` tag and -may contain any number of script code statements, even entire programs. - -It is allowed to omit the closing `%}` of a statement block to parse the -entire remaining source text after the opening tag as ucode script. - -By default, statement blocks produce no output and the entire block is -reduced to an empty string during template evaluation but contained script -code might invoke functions such as `print()` to explicitly output contents. - -For example the following template would result in `The epoch is odd` or -`The epoch is even`, depending on the current epoch value: - -`The epoch is {% if (time() % 2): %}odd{% else %}even{% endif %}!` - - -#### 2. Expression block - -Expression blocks are enclosed in an opening `{{` and a closing `}}` tag and -may only contain a single expression statement (multiple expressions may be -chained with comma). The implicit result of the rightmost evaluated expression -is used as output when processing the block. - -For example the template `Hello world, {{ getenv("USER") }}!` would result in -the output "Hello world, user!" where `user` would correspond to the name of -the current user executing the ucode interpreter. - - -#### 3. Comment block - -Comment blocks, which are denoted with an opening `{#` and a closing `#}` tag -may contain arbitrary text except the closing `#}` tag itself. Comments blocks -are completely stripped during processing and are replaced with an empty string. - -The following example template would result in the output "Hello world": - -`Hello {# mad #}word` - - -### Whitespace handling - -Each block start tag may be suffixed with a dash to strip any whitespace -before the block and likewise any block end tag may be prefixed with a dash -to strip any whitespace following the block. - -Without using whitespace stripping, the following example: - -``` -This is a first line -{% for (x in [1, 2, 3]): %} -This is item {{ x }}. -{% endfor %} -This is the last line -``` - -Would result in the following output: - -``` -This is a first line - -This is item 1. -This is item 2. -This is item 3. - -This is the last line -``` - -By adding a trailing dash to apply whitespace stripping after the block, the -empty lines can be eliminated: - -``` -This is a first line -{% for (x in [1, 2, 3]): -%} -This is item {{ x }}. -{% endfor -%} -This is the last line -``` - -Output: - -``` -This is a first line -This is item 1. -This is item 2. -This is item 3. -This is the last line -``` - -By applying whitespace stripping before the block, all lines can be joined -into a single output line: - -``` -This is a first line -{%- for (x in [1, 2, 3]): -%} -This is item {{ x }}. -{%- endfor -%} -This is the last line -``` - -Output: - -``` -This is a first lineThis is item 1.This is item 2.This is item 3.This is the last line -``` - -## Script syntax - -The ucode script language - used either within statement and expression blocks -or throughout the entire file in *raw mode*, uses untyped variables and employs -a simplified JavaScript like syntax. - -The language implements function scoping and differentiates between local and -global variables. Each function has its own private scope while executing and -local variables declared inside a function are not accessible in the outer -calling scope. - -### 1. Data types - -Ucode supports seven different basic types as well as two additional special -types; function values and ressource values. The supported types are: - - - Boolean values (`true` or `false`) - - Integer values (`-9223372036854775808` to `+9223372036854775807`) - - Double values (`-1.7e308` to `+1.7e308`) - - String values (e.g. `'Hello world!'` or `"Sunshine \u2600!"`) - - Array values (e.g. `[1, false, "foo"]`) - - Object values (e.g. `{ foo: true, "bar": 123 }`) - - Null value (`null`) - -Ucode utilizes reference counting to manage memory used for variables and values -and frees data automatically as soon as values go out of scope. - -Numeric values are either stored as signed 64bit integers or as IEEE 756 double -value. Conversion between integer and double values can happen implicitly, e.g. -through numeric operations, or explicitely, e.g. by invoking functions such as -`int()`. - -### 2. Variables - -Variable names must start with a letter or an underscore and may only contain -the characters `A`..`Z`, `a`..`z`, `0`..`9` or `_`. By prefixing a variable -name with the keyword `let`, it is declared in the local block scope only -and not visible outside anymore. - -Variables may also be declared using the `const` keyword. Such variables follow -the same scoping rules as `let` declared ones but they cannot be modified after -they have been declared. Any attempt to do so will result in a syntax error -during compilation. - -```javascript -{% - - a = 1; // global variable assignment - - function test() { - let b = 2; // declare `b` as local variable - a = 2; // overwrite global a - } - - test(); - - print(a, "\n"); // outputs "2" - print(b, "\n"); // outputs nothing - - const c = 3; - print(c, "\n"); // outputs "3" - - c = 4; // raises syntax error - c++; // raises syntax error - - const d; // raises syntax error, const variables must - // be initialized at declaration time - -%} -``` - -### 3. Control statements - -Similar to JavaScript, ucode supports `if`, `for` and `while` statements to -control execution flow. - -#### 3.1. Conditional statement - -If/else blocks can be used to execute statements depending on a condition. - -```javascript -{% - - user = getenv("USER"); - - if (user == "alice") { - print("Hello Alice!\n"); - } - else if (user == "bob") { - print("Hello Bob!\n"); - } - else { - print("Hello guest!\n"); - } - -%} -``` - -If only a single statement is wrapped by an if or else branch, the enclosing -curly braces may be omitted: - -```javascript -{% - - if (rand() == 3) - print("This is quite unlikely\n"); - -%} -``` - -#### 3.2. Loop statements - -Ucode script supports three different flavors of loop control statements; a -`while` loop that executes enclosed statements as long as the loop condition is -fulfilled, a `for in` loop that iterates keys of objects or items of arrays and -a counting `for` loop that is a variation of the `while` loop. - -```javascript -{% - - i = 0; - arr = [1, 2, 3]; - obj = { Alice: 32, Bob: 54 }; - - // execute as long as condition is true - while (i < length(arr)) { - print(arr[i], "\n"); - i++; - } - - // execute for each item in arr - for (n in arr) { - print(n, "\n"); - } - - // execute for each key in obj - for (person in obj) { - print(person, " is ", obj[person], " years old.\n"); - } - - // execute initialization statement (j = 0) once - // execute as long as condition (j < length(arr)) is true - // execute step statement (j++) after each iteration - for (j = 0; j < length(arr); j++) { - print(arr[j], "\n"); - } - -%} -``` - -#### 3.3. Alternative syntax - -Since conditional statements and loops are often used for template formatting -purposes, e.g. to repeat a specific markup for each item of a list, ucode -supports an alternative syntax that does not require curly braces to group -statements but that uses explicit end keywords to denote the end of the control -statement body for better readability instead. - -The following two examples first illustrate the normal syntax, followed by the -alternative syntax that is more suitable for statement blocks: - -``` -Printing a list: -{% for (n in [1, 2, 3]) { -%} - - Item #{{ n }} -{% } %} -``` - -The alternative syntax replaces the opening curly brace (`{`) with a colon -(`:`) and the closing curly brace (`}`) with an explicit `endfor` keyword: - -``` -Printing a list: -{% for (n in [1, 2, 3]): -%} - - Item #{{ n }} -{% endfor %} -``` - -For each control statement type, a corresponding alternative end keyword is defined: - - - `if (...): ... endif` - - `for (...): ... endfor` - - `while (...): ... endwhile` - - -### 4. Functions - -Ucode scripts may define functions to group repeating operations into reusable -operations. Functions can be both declared with a name, in which case they're -automatically registered in the current scope, or anonymously which allows -assigning the resulting value to a variable, e.g. to build arrays or objects of -functions: - -```javascript -{% - - function duplicate(n) { - return n * 2; - } - - let utilities = { - concat: function(a, b) { - return "" + a + b; - }, - greeting: function() { - return "Hello, " + getenv("USER") + "!"; - } - }; - --%} - -The duplicate of 2 is {{ duplicate(2) }}. -The concatenation of 'abc' and 123 is {{ utilities.concat("abc", 123) }}. -Your personal greeting is: {{ utilities.greeting() }}. -``` - -#### 4.1. Alternative syntax - -Function declarations support the same kind of alternative syntax as defined -for control statements (3.3.) - -The alternative syntax replaces the opening curly brace (`{`) with a colon -(`:`) and the closing curly brace (`}`) with an explicit `endfunction` -keyword: - -``` -{% function printgreeting(name): -%} - Hallo {{ name }}, nice to meet you. -{% endfunction -%} - -<h1>{{ printgreeting("Alice") }}</h1> -``` - - -### 5. Operators - -Similar to JavaScript and C, ucode scripts support a range of different -operators to manipulate values and variables. - -#### 5.1. Arithmetic operations - -The operators `+`, `-`, `*`, `/`, `%`, `++` and `--` allow to perform -additions, substractions, multiplications, divisions, modulo, increment or -decrement operations respectively where the result depends on the type of -involved values. - -The `++` and `--` operators are unary, means that they only apply to one -operand. The `+` and `-` operators may be used in unary context to either -convert a given value to a numeric value or to negate a given value. - -If either operand of the `+` operator is a string, the other one is converted -to a string value as well and a concatenated string is returned. - -All other arithmetic operators coerce their operands into numeric values. -Fractional values are converted to doubles, other numeric values to integers. - -If either operand is a double, the other one is converted to a double value as -well and a double result is returned. - -Divisions by zero result in the special double value `Infinity`. If an operand -cannot be converted to a numeric value, the result of the operation is the -special double value `NaN`. - -```javascript -{% - a = 2; - b = 5.2; - s1 = "125"; - s2 = "Hello world"; - - print(+s1); // 125 - print(+s2); // NaN - print(-s1); // -125 - print(-s2); // NaN - print(-a); // -2 - - print(a++); // 2 (Return value of a, then increment by 1) - print(++a); // 4 (Increment by 1, then return value of a) - - print(b--); // 5.2 (Return value of b, then decrement by 1) - print(--b); // 3.2 (Decrement by 1, then return value of b) - - print(4 + 8); // 12 - print(7 - 4); // 3 - print(3 * 3); // 9 - - print(10 / 4); // 2 (Integer division) - print(10 / 4.0); // 2.5 (Double division) - print(10 / 0); // Infinity - - print(10 % 7); // 3 - print(10 % 7.0); // NaN (Modulo is undefined for non-integers) -%} -``` - -#### 5.2. Bitwise operations - -The operators `&`, `|`, `^`, `<<`, `>>` and `~` allow to perform bitwise and, -or, xor, left shift, right shift and complement operations respectively. - -The `~` operator is unary, means that is only applies to one operand. - -```javascript -{% - print(0 & 0, 0 & 1, 1 & 1); // 001 - print(0 | 0, 0 | 1, 1 | 1); // 011 - print(0 ^ 0, 0 ^ 1, 1 ^ 1); // 010 - print(10 << 2); // 40 - print(10 >> 2); // 2 - print(~15); // -16 (0xFFFFFFFFFFFFFFF0) -%} -``` - -An important property of bitwise operators is that they're coercing their -operand values to whole integers: - -```javascript -{% - print(12.34 >> 0); // 12 - print(~(~12.34)); // 12 -%} -``` - -#### 5.3. Relational operations - -The operators `==`, `!=`, `<`, `<=`, `>` and `>=` test whether their operands -are equal, inequal, lower than, lower than/equal to, higher than or higher -than/equal to each other respectively. - -If both operands are strings, their respective byte values are compared, if -both are objects or arrays, their underlying memory addresses are compared. - -In all other cases, both operands are coerced into numeric values and the -resulting values are compared with each other. - -This means that comparing values of different types will coerce them both to -numbers. - -The result of the relational operation is a boolean indicating truishness. - -```javascript -{% - print(123 == 123); // true - print(123 == "123"); // true! - print(123 < 456); // true - print(123 > 456); // false - print(123 != 456); // true - print(123 != "123"); // false! - print({} == {}); // false (two different anonymous objects) - a = {}; print(a == a); // true (same object) -%} -``` - -#### 5.4. Logical operations - -The operators `&&`, `||`, `??` and `!` test whether their operands are all true, -partially true, null or false respectively. - -In the case of `&&` the rightmost value is returned while `||` results in the -first truish and `??` in the first non-null value. - -The unary `!` operator will result in `true` if the operand is not trueish, -otherwise it will result in `false`. - -Operands are evaluated from left to right while testing truishness, which means -that expressions with side effects, such as function calls, are only executed -if the preceeding condition was satisifed. - -```javascript -{% - print(1 && 2 && 3); // 3 - print(1 || 2 || 3); // 1 - print(2 > 1 && 3 < 4); // true - print(doesnotexist ?? null ?? 42); // 42 - print(1 ?? 2 ?? 3); // 1 - print(!false); // true - print(!true); // false - - res = test1() && test2(); // test2() is only called if test1() returns true -%} -``` - -#### 5.5. Assignment operations - -In addition to the basic assignment operator `=`, most other operators have a -corresponding shortcut assignment operator which reads the specified variable, -applies the operation and operand to it, and writes it back. - -The result of assignment expressions is the assigned value. - -```javascript -{% - a = 1; // assign 1 to variable a - a += 2; // a = a + 2; - a -= 3; // a = a - 3; - a *= 4; // a = a * 4; - a /= 5; // a = a / 5; - a %= 6; // a = a % 6; - a &= 7; // a = a & 7; - a |= 8; // a = a | 8; - a ^= 9; // a = a ^ 9; - a <<= 10; // a = a << 10; - a >>= 11; // a = a >> 11; - a &&= 12; // a = a && 12; - a ||= 13; // a = a || 13; - a ??= 14; // a = a ?? 14; - - print(a = 2); // 2 -%} -``` - -#### 5.6. Miscellaneous operators - -Besides the operators described so far, ucode script also supports a `delete` -operator which removes a property from an object value. - -```javascript -{% - a = { test: true }; - - delete a.test; // true - delete a.notexisting; // false - - print(a); // { } -%} -``` - -### 6. Functions - -Ucode scripts may call a number of built-in functions to manipulate values or -to output information. - -#### 6.1. `abs(x)` - -Returns the absolute value of the given operand. Results in `NaN` if operand is -not convertible to number. - -```javascript -abs(1); // 1 -abs(-2); // 2 -abs(-3.5); // 3.5 -abs("0x123"); // 291 -abs("-0x123"); // NaN -abs([]); // NaN -``` - -#### 6.2. `atan2(x, y)` - -Calculates the principal value of the arc tangent of x/y, using the signs of -the two arguments to determine the quadrant of the result. - -#### 6.3. `chr(n1, ...)` - -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. - -```javascript -chr(65, 98, 99); // "Abc" -chr(-1, 300); // string consisting of an `0x0` and a `0xff` byte -``` - -#### 6.4. `cos(x)` - -Return the cosine of x, where x is given in radians. - -#### 6.5. `die(msg)` - -Raise an exception with the given message and abort execution. - -#### 6.6. `exists(obj, key)` - -Return `true` if the given key is present within the object passed as first -argument, otherwise `false`. - -#### 6.7. `exit(n)` - -Terminate the interpreter with the given exit code. - -#### 6.8. `exp(n)` - -Return the value of e (the base of natural logarithms) raised to the power -of n. - -#### 6.9. `filter(arr, fn)` - -Filter the array passed as first argument by invoking the function specified -in the second argument for each array item. - -If the invoked function returns a truish 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 the filtered array. - -```javascript -// 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] -``` - -#### 6.10. `getenv([name])` - -Return the value of the given environment variable. If the variable name is -omitted, returns a dictionary containing all environment variables. - -#### 6.11. `hex(x)` - -Convert the given hexadecimal string into a number. - -#### 6.12. `index(arr_or_str, needle)` - -Find the given value passed as 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. - -#### 6.13. `int(x)` - -Convert the given value to an integer. Returns `NaN` if the value is not -convertible. - -#### 6.14. `join(sep, arr)` - -Join the array passed as 2nd argument into a string, using the separator passed -in the first argument as glue. Returns `null` if the second argument is not an -array. - -#### 6.15. `keys(obj)` - -Return an array of all key names present in the passed object. Returns `null` -if the given argument is no object. - -#### 6.16. `lc(s)` - -Convert the given string to lowercase and return the resulting string. -Returns `null` if the given argument could not be converted to a string. - -#### 6.17. `length(x)` - -Return the length of the given object, array or string. Returns `null` if -the given argument is neither an object, array, nor a string. - -For objects, the length is defined as the number of keys within the object, -for arrays the length specifies the amount of contained items and for strings -it represents the number of contained bytes. - -```javascript -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 -``` - -#### 6.18. `log(x)` - -Return the natural logarithm of x. - -#### 6.19. `ltrim(s, c)` - -Trim any of the specified characters in `c` from the start of `str`. -If the second argument is omitted, trims the characters ` ` (space), `\t`, -`\r` and `\n`. - -```javascript -ltrim(" foo \n") // "foo \n" -ltrim("--bar--", "-") // "bar--" -``` - -#### 6.20. `map(arr, fn)` - -Transform the array passed as first argument by invoking the function specified -in the second argument for each array item. - -The result of the invoked function is put into the resulting array. -The map function is invoked with three arguments: - - 1. The array value - 2. The current index - 3. The array being filtered - -Returns the transformed array. - -```javascript -// turn into 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"] -``` - -#### 6.21. `ord(s [, offset])` - -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. - -```javascript -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 -``` - -#### 6.22. `pop(arr)` - -Pops the last item from the given array and returns it. Returns `null` if the -array was empty or if a non-array argument was passed. - -#### 6.23. `print(x, ...)` - -Print any of the given values to stdout. Arrays and objects are converted to -their JSON representation. - -Returns the amount of bytes printed. - -#### 6.24. `push(arr, v1, ...)` - -Push the given argument(s) to the given array. Returns the last pushed value. - -#### 6.25. `rand()` - -Returns a random number. If `srand()` has not been called already, it is -automatically invoked passing the current time as seed. - -#### 6.26. `reverse(arr_or_str)` - -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 `null` if neither an array nor a string were passed. - -#### 6.27. `rindex(arr_or_str, needle)` - -Find the given value passed as 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. - -#### 6.28. `rtrim(str, c)` - -Trim any of the specified characters in `c` from the end of `str`. -If the second argument is omitted, trims the characters, ` ` (space), `\t`, -`\r` and `\n`. - -```javascript -rtrim(" foo \n") // " foo" -rtrim("--bar--", "-") // "--bar" -``` - -#### 6.29. `shift(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. - -#### 6.30. `sin(x)` - -Return the sine of x, where x is given in radians. - -#### 6.31. `sort(val, fn)` - -Sort the given array or object according to the given comparison function. -If no comparison function is provided, a default lexical sort order is -applied. - -Values are sorted in-place, the given array of object value is modified -by this function. - -Sorting of objects is performed by reordering the internal key sequence -without modifying or rehashing the contained values themselves. Since -ucode maintains insertion order of keys within object values, this is -useful to sort object keys for output purposes. - -The comparison function is repeatedly invoked until the array values -or object key sequence is fully sorted. It should return a numeric -value less than, equal to or greater than zero when the first item -is smaller, equal or larger than the second one respectively. - -When sorting array values, the comparison function is invoked with two -array value arguments to compare against each other. - -When sorting object values, the comparison function is invoked with four -arguments; two keys to compare to each other as well as their -corresponding object values. - -Returns the given, ordered array or object value. - -Returns `null` if the given argument was neither an array nor object. - -Throws an exception if a non-callable custom comparison function was -provided or if the comparison function triggered an exception. - -```javascript -sort([8, 1, 5, 9]) // [1, 5, 8, 9] -sort(["Bean", "Orange", "Apple"], function(a, b) { - return length(a) - length(b); -}) // ["Bean", "Apple", "Orange"] - -sort({ qrx: 1, foo: 2, abc: 3 }) // { "abc": 3, "foo": 2, "qrx": 1 } -sort({ a: 5, b: 3, c: 2, d: 4, e: 1 }, function(k1, k2, v1, v2) { - return v1 - v2; -}) // { "e": 1, "c": 2, "b": 3, "d": 4, "a": 5 } -sort({ "Bean": true, "Orange": true, "Apple": true }, function(k1, k2) { - return length(k1) - length(k2); -}) // { "Bean": true, "Apple": true, "Orange": true } -``` - -#### 6.32. `splice(arr, off, len, ...)` - -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. - -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. `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` if 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. - -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. - -```javascript -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"] -``` - -#### 6.35. `sqrt(x)` - -Return the nonnegative square root of x. - -#### 6.36. `srand(n)` - -Seed the PRNG using the given number. - -#### 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. -If `len` is omitted, returns everything through the end of the string. If `len` -is negative, leaves that many characters off the end of the string. - -```javascript -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 -``` - -#### 6.38. `time()` - -Returns the current UNIX epoch. - -```javascript -time(); // 1598043054 -``` - -#### 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`, -`\r` and `\n`. - -```javascript -trim(" foo \n") // "foo" -trim("--bar--", "-") // "bar" -``` - -#### 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.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` .. -`0x10FFFF` are represented by the unicode replacement character `0xFFFD`. - -```javascript -uchr(0x2600, 0x26C6, 0x2601); // "☀⛆☁" -uchr(-1, 0x20ffff, "foo"); // "���" -``` - -#### 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.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.44. `values(obj)` - -Returns an array containing all values of the given object. Returns `null` if -no object was passed. - -```javascript -values({ foo: true, bar: false }); // [true, false] -``` - -#### 6.45. `printf(fmt, ...)` - -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. - -```javascript -{% - 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 - // ] -%} -``` - -#### 6.46. `sprintf(fmt, ...)` - -Formats the given arguments according to the given format string and returns the -resulting string. - -See `printf()` for details. - -#### 6.47. `match(str, /pattern/)` - -Match the given string against the regular expression pattern specified as -second argument. - -If the passed regular expression uses the `g` flag, the return value will be an -array of arrays describing all found occurences 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. - -```javascript -match("foobarbaz", /b.(.)/) // ["bar", "r"] -match("foobarbaz", /b.(.)/g) // [["bar", "r"], ["baz", "z"]] -``` - -#### 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 -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 occurence 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 first and any capture group values as subsequent parameters. - -If the replace value is a string, the following special substrings are -substituted before it is inserted into the result: - - - `$$` - replaced by a literal `$` - - ``$` `` - replaced by the text before the match - - `$'` - replaced by the text after the match - - `$&` - replaced by the matched substring - - `$1`..`$9` - replaced by the value of the corresponding capture group, if the capture group is not defined, it is not substituted - -```javascript -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 -``` - -#### 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. - -```javascript -json('{"a":true, "b":123}') // { "a": true, "b": 123 } -json('[1,2,') // Throws exception -``` - -#### 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. - -By default, the file is executed within the same scope as the calling -`include()` but by passing an object as 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 explicitely -provided properties, the `proto()` function can be used to create a scope -object with an empty prototype. See the examples below for details. - -If the given path argument is not absolute, it is interpreted relative to the -directory of the current template file, that is the file that is invoking the -`include()` function. - -If the ucode interpreter executes program code from stdin, the given path is -interpreted relative to the current working directory of the process. - -```javascript -// 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 -}, {})) -``` - -#### 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.52. `system(command, timeout)` - -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 programs -`exit()` code is returned. If the program was terminated by an uncatched -signal, a negative signal number is returned, e.g. `-9` when the program was -terminated by `SIGKILL`. - -If the optional timeout argument is specified, the program is terminated by -`SIGKILL` after that many milliseconds when it didn't complete within the timeout. - -Omitting the timeout argument, or passing `0` disables the command timeout. - -```javascript -// Execute through `/bin/sh` -system("echo 'Hello world' && exit 3"); // prints "Hello world" to stdout and returns 3 - -// Execute argument vector -system(["/usr/bin/date", "+%s"]); // prints the UNIX timestamp to stdout and returns 0 - -// Apply a timeout -system("sleep 3 && echo 'Success'", 1000); // returns -9 -``` - -#### 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 -as the program is executed. - -Invoking `trace()` with zero as argument will turn off opcode tracing. - -Right now, any positive non-zero value will enable tracing while future -implementation might provide different different verbosity levels or treat -the level argument as bit mask to enable or disable individual debug -elements. - -#### 6.54. `proto(val[, proto])` - -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 prototype on the array or object in `val`. - -Throws an exception if the given prototype value is not an object. - -#### 6.55. `sleep(milliseconds)` - -Pause execution for the given amount of milliseconds. Returns `false` if -an invalid value was passed, otherwise `true`. - -#### 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.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 -returns the captured contents. The second argument is treated as scope. See -`include()` for details on scoping. - -When invoked with a function value as first argument, `render()` calls the -given function and passes all subsequent arguments to it. Any output -(through print(), template text instructions and the like) produced by the -called function is captured and returned as string. The return value of the -called function is discarded. - -#### 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. - -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 by the underlying C runtimes `regcomp(3)` function. - -Returns the compiled regular expression value. - -```javascript -regexp('foo.*bar', 'is'); // equivalent to /foo.*bar/is -regexp('foo.*bar', 'x'); // throws "Type error: Unrecognized flag character 'x'" -regexp('foo.*('); // throws "Syntax error: Unmatched ( or \(" -``` - -#### 6.59. `wildcard(subject, pattern[, nocase])` - -Match the given subject against the supplied wildcard (file glob) pattern. - -If a truish value is supplied as 3rd argument, case insensitive matching is -performed. If a non-string value is supplied as subject, it is converted into -a string before being matched. - -Returns `true` when the subject matches the pattern or `false` when not. - -#### 6.60. `sourcepath([depth [, dironly]])` - -Determine the path of the source file currently being executed by ucode. - -The optional `depth` parameter allows walking up the call stack to determine -the path of the parent sources including or requiring the current source file. -If unspecified, the `depth` defaults to `0`, that is the currently executed -file. - -If a truish value is passed in `dironly`, only the directory portion of the -source file path is returned. - -If the ucode interpreter executes code from stdin or a code fragment passed -via `-s` switch, the function returns `null` since there is no associated -file path. - -If `depth` exceeds the size of the call stack, the function returns `null` -as well. - -#### 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 -the same value coercion rules as for relational operators apply. If both -strings and numbers are passed to `min()`, then any string values will be -effectively ignored since both `1 < "abc"` and `1 > "abc"` comparisons -yield false results. - -```javascript -min(5, 2.1, 3, "abc", 0.3); // 0.3 -min(1, "abc"); // 1 -min("1", "abc"); // "1" -min("def", "abc", "ghi"); // "abc" -min(true, false); // false -``` - -#### 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 -the same value coercion rules as for relational operators apply. If both -strings and numbers are passed to `min()`, then any string values will be -effectively ignored since both `1 < "abc"` and `1 > "abc"` comparisons -yield false results. - -```javascript -max(5, 2.1, 3, "abc", 0.3); // 5 -max(1, "abc"); // 1 (!) -max("1", "abc"); // "abc" -max("def", "abc", "ghi"); // "ghi" -max(true, false); // true -``` - -#### 6.63. `b64dec(str)` - -Decodes the given base64 encoded string and returns the decoded result, any -whitespace in the input string is ignored. - -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`. - -```javascript -b64dec("VGhpcyBpcyBhIHRlc3Q="); // "This is a test" -b64dec(123); // null -b64dec("XXX"); // null -``` - -#### 6.64. `b64enc(str)` - -Encodes the given string into base64 and returns the resulting encoded -string. - -If a non-string argument is given, the function returns `null`. - -```javascript -b64enc("This is a test"); // "VGhpcyBpcyBhIHRlc3Q=" -b64enc(123); // null -``` - -#### 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 -are simply skipped. - -If a non-array argument is given, the function returns `null`. - -```javascript -uniq([ 1, true, "foo", 2, true, "bar", "foo" ]); // [ 1, true, "foo", 2, "bar" ] -uniq("test"); // null -``` - -#### 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 -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. - -```javascript -localtime(1647953502); -// { -// "sec": 42, -// "min": 51, -// "hour": 13, -// "mday": 22, -// "mon": 3, -// "year": 2022, -// "wday": 2, -// "yday": 81, -// "isdst": 0 -// } -``` - -#### 6.67. `gmtime([epoch])` - -Like `localtime()` but interpreting the given epoch value as UTC time. - -See `localtime()` for details on the return value. - -#### 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 -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. - -```javascript -timelocal({ "sec": 42, "min": 51, "hour": 13, "mday": 22, "mon": 3, "year": 2022, "isdst": 0 }) -// 1647953502 -``` - -#### 6.69. `timegm(datetimespec)` - -Like `timelocal()` but interpreting the given date time specification as -UTC time. - -See `timelocal()` for details. - -#### 6.70. `clock([monotonic])` - -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 first and -the nanosecond fraction as second element. - -Returns `null` if a monotonic clock value is requested and the system -does not implement this clock type. - -```javascript -clock(); // [ 1647954926, 798269464 ] -clock(true); // [ 474751, 527959975 ] -``` - -#### 6.71. `hexdec(hexstring[, skipchars])` - -The `hexdec()` function 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. - -```javascript -hexdec("48656c6c6f20776f726c64210a"); // "Hello world!\n" -hexdec("44:55:66:77:33:44", ":"); // "DUfw3D" -``` - -#### 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. - -Returns the encoded hexadecimal digit string. - -```javascript -hexenc("Hello world!\n"); // "48656c6c6f20776f726c64210a" -``` - -#### 6.73. `gc([operation[, argument]])` - -The `gc()` function allows interaction 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`. - -Returns `null` if a non-string `operation` value is given. - -#### 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 -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. - -```javascript -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 -``` - -#### 6.75. `loadfile(path[, options])` - -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. - -```javascript -loadfile("./templates/example.uc"); // function main() { ... } -``` - -#### 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 -`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. - -```javascript -// 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 -``` +Projects using ucode scripting include the +[OpenWrt LuCI web interface](https://github.com/openwrt/luci) and the +[OpenWrt firewall4 framework](https://github.com/openwrt/firewall4). |