Repository navigation
Standard Library
- IO / Introspection
- Loading / Modules
- Error Handling
- Constructors
- Length
- Iteration
- List Ops
- Map Ops
- Set Ops
- Data Quality-of-Life
- String Ops
- List Utilities
- Advanced List Operations
- Random Utilities
- String Padding and Formatting
- Path Ops
- Date / Time
- Filesystem
- Advanced File Operations
- Formatting
- JSON
- Data Format Functions
- Time
- Promises
- Math Functions
- Math Constants
- Conversion
- Memory Management
- Safety Controls
- Network I/O
These functions are registered by cs_register_stdlib(vm).
Prints values separated by spaces and ends with newline.
Returns one of:
"nil" "bool" "int" "float" "string" "bytes" "list" "map" "set" "strbuf" "range" "function" "native" "promise"
Boolean type-checking functions:
is_nil(value) -> boolis_bool(value) -> boolis_int(value) -> boolis_float(value) -> boolis_string(value) -> boolis_set(value) -> boolis_bytes(value) -> boolis_list(value) -> boolis_map(value) -> boolis_function(value) -> boolis_promise(value) -> bool
Example:
if (is_int(x) || is_float(x)) {
print("x is a number");
}Returns environment variable value or nil if missing.
- If
condis falsy → runtime error - Optional second argument string overrides error message
Runs an external command and captures combined stdout/stderr.
Returns a map:
-
code- exit code (int) -
out- captured output (string)
Notes:
-
argsis an optional list of arguments. - Output is captured as raw bytes into a string.
Example:
let r = subprocess("echo", ["hello"]);
print(r.code); // 0
print(r.out); // "hello\n"Runs a file. Returns true on success, nil/error on failure.
Runs a file once per resolved path.
- Subsequent calls with the same resolved path return the cached exports without re-running.
- Returns the module's
exportsmap.
Like require(), but returns nil if the file doesn't exist instead of throwing an error.
If the file exists, it behaves like require() and returns the module exports.
Creates a standardized error object with:
-
msg- Error message -
code- Error code (defaults to"ERROR"if omitted) -
stack- List of stack frame strings
Returns true if value is an error object (has msg and code fields).
Example:
fn validate(x) {
if (x < 0) {
throw error("Invalid value: must be non-negative", "INVALID_ARG");
}
}
try {
validate(-5);
} catch (e) {
if (is_error(e)) {
print("Error:", e.msg);
print("Code:", e.code);
}
}Formats an error object (as produced by error()) into a human-readable string.
Current format is a single line: [CODE] message. Stack traces are available on err.stack.
Global map of common error codes (strings), intended to be used with error(msg, code).
Creates a set (unique collection of values).
- no args → empty set
- list → adds each element (duplicates ignored)
- map → adds each key
- set → copies values
Creates a bytes buffer.
Accepted inputs:
- no args → empty bytes
- int → length (zero-filled)
- string → raw byte copy of the string
- list of ints 0..255 → bytes filled from list
- bytes → returns a copy
Works for:
- string: number of bytes
- bytes: byte length
- list: element count
- tuple: field count
- map: entry count
- set: entry count
- strbuf: byte length Else returns 0.
Returns a lazy range object (iterable) without allocating a list:
-
range(5)→ yields0, 1, 2, 3, 4 -
range(2, 7)→ yields2, 3, 4, 5, 6 -
range(0, 10, 2)→ yields0, 2, 4, 6, 8 -
range(10, 0, -2)→ yields10, 8, 6, 4, 2
Default step is 1. Step cannot be 0.
Example:
for i in range(10) {
print(i);
}Returns a list of [index, value] pairs. Use with destructuring for Pythonic iteration:
let items = ["a", "b", "c"]
// Pythonic style with destructuring
for [idx, val] in enumerate(items) {
print("${idx}: ${val}")
// Output: 0: a, 1: b, 2: c
}
// Native two-variable syntax (VALUE FIRST, then INDEX)
for val, idx in items {
print("${idx}: ${val}")
// Output: 0: a, 1: b, 2: c
}
// Manual destructuring
let pairs = enumerate(items)
for pair in pairs {
let [idx, val] = pair
print("${idx}: ${val}")
}Important: CupidScript's native for val, idx in list syntax uses value-first order, while enumerate() returns index-first [idx, val] pairs to match Python conventions. Use the appropriate syntax for your use case.
Examples:
let xs = ["a", "b", "c"];
print(enumerate(xs)); // [[0, "a"], [1, "b"], [2, "c"]]
// Use with destructuring in comprehensions
let labeled = [to_str(i) + ": " + v for [i, v] in enumerate(xs)];
// ["0: a", "1: b", "2: c"]
// Create index-to-value map
let index_map = {i: v for [i, v] in enumerate(xs)};
// {0: "a", 1: "b", 2: "c"}
// Filter based on index
let odd_indices = [v for [i, v] in enumerate(xs) if i % 2 != 0];
// ["b"]
// Native syntax: value comes first
for val, idx in xs {
print("Item ${idx} is ${val}");
// Item 0 is a
// Item 1 is b
// Item 2 is c
}Iteration Order Summary:
| Syntax | Order | Example |
|---|---|---|
for val, idx in list |
Value, Index |
for x, i in [10, 20] → x=10,i=0 then x=20,i=1 |
enumerate(list) |
[Index, Value] |
enumerate([10, 20]) → [[0, 10], [1, 20]]
|
for [idx, val] in enumerate(list) |
Index, Value (destructured) | Matches Python's enumerate()
|
Best Practice:
- Use native
for val, idx in listwhen you primarily need the value - Use
enumerate()with destructuring when porting Python code or when you want explicit index-first ordering
Pairs elements by index up to the shorter list length.
print(zip([1, 2], ["x", "y", "z"])); // [[1, "x"], [2, "y"]]Returns true if any element is truthy (or satisfies pred).
any([nil, 0, 3]) // true
any([1, 3, 5], fn(x) => x % 2 == 0) // falseReturns true if all elements are truthy (or satisfy pred).
all([1, 2, 3]) // true
all([1, 2, 3], fn(x) => x < 3) // falseReturns a new list with elements that satisfy pred.
filter([1, 2, 3, 4], fn(x) => x % 2 == 0) // [2, 4]Returns a new list with mapper applied to each element.
map([1, 2, 3], fn(x) => x * 2) // [2, 4, 6]Reduces a list left-to-right. If init is omitted, uses the first element.
reduce([1, 2, 3], fn(a, b) => a + b) // 6
reduce([1, 2, 3], fn(a, b) => a + b, 10) // 16Inserts value at position index, shifting existing elements.
Removes and returns the element at index.
Returns a new list containing length elements starting at start.
Deletes a key from the map.
Returns a list of all values in the map.
Returns a list of [key, value] pairs.
CupidScript provides both modern literal syntax and legacy function-based operations for sets.
// Create sets
let s = #{1, 2, 3};
let empty = #{};
// Set comprehensions
let evens = #{x for x in range(20) if x % 2 == 0};
// Methods
s.add(4); // returns true if added
s.contains(3); // true
s.remove(2); // returns true if removed
s.size(); // 3
s.clear(); // empties the set
// Operators
let a = #{1, 2, 3};
let b = #{2, 3, 4};
a | b; // union: #{1, 2, 3, 4}
a & b; // intersection: #{2, 3}
a - b; // difference: #{1}
a ^ b; // symmetric difference: #{1, 4}See Collections - Sets for complete documentation.
These functions remain available for compatibility:
Adds a value. Returns true if inserted, false if it was already present.
Returns true if value exists.
Removes a value. Returns true if removed.
Returns a list of values in the set (iteration order is unspecified).
Alias for values(). Returns all values as a list for convenient iteration.
Returns a shallow copy:
- Lists: creates new list with same elements
- Maps: creates new map with same key-value pairs
- Sets: creates new set with same values
- Other types: returns the value as-is
Returns a deep copy with cycle detection:
- Lists, maps, and sets are recursively copied
- Handles self-referential structures
- Other types: returns the value as-is
Reverses a list in-place.
Returns a new reversed list without modifying the original.
Checks if item exists:
- Lists: checks for element
- Maps: checks for key
- Sets: checks for value
- Strings: checks for substring
Example:
let original = [1, [2, 3], 4];
let shallow = copy(original);
let deep = deepcopy(original);
shallow[1][0] = 99; // Modifies original[1][0]
deep[1][1] = 88; // Does not affect original
reverse([1, 2, 3]); // Returns [3, 2, 1], in-place
let rev = reversed([1, 2, 3]); // original unchanged
print(contains([1, 2, 3], 2)); // true
print(contains({"a": 1}, "a")); // true
print(contains(set([1, 2]), 2)); // true
print(contains("hello", "ell")); // trueReturns first index or -1.
Replaces all occurrences of old with rep.
- If
oldis empty, returns original string.
Splits on a string separator.
- If
sepis empty, returns[s].
Returns true if sub appears anywhere in s.
Counts non-overlapping occurrences of sub. If sub is empty, returns 0.
Pads on the left until width is reached.
Pads on the right until width is reached.
Returns the reversed string (byte-wise).
Regex uses POSIX Extended Regular Expressions (ERE). Invalid patterns raise a runtime error. On Windows builds, regex functions may be unavailable.
Returns true if any match is found.
Returns true only if the entire string matches.
Returns a match object or nil if no match:
-
start(int) - byte index of match start -
end(int) - byte index of match end -
match(string) - matched text -
groups(list) - capture groups (nilfor unmatched groups)
Returns a list of match objects with the same shape as regex_find.
Replaces all matches with a literal replacement string. Capture expansion (like $1) is not supported.
Example:
let text = "foo-123-bar";
print(regex_is_match("[0-9]+", text));
let m = regex_find("([0-9]+)", text);
print(m.start, m.end, m["match"], m.groups[0]);
let all = regex_find_all("[0-9]+", "a1 b22 c333");
print(len(all));
print(regex_replace("[0-9]+", "a1b22c", "#"));Returns substring starting at start with length characters.
Joins list elements into a string with the given separator.
Returns a new list with duplicates removed (preserves order).
Flattens one level of nested lists.
Splits into sublists of at most size elements.
Removes nil values.
Sums numeric elements (ignores nil). Returns nil if any non-number is present.
Returns a new list containing elements that appear in both lists (no duplicates).
Example:
let a = [1, 2, 3, 4];
let b = [3, 4, 5, 6];
print(list_intersection(a, b)); // [3, 4]Returns a new list containing elements in list1 that are not in list2.
Example:
let a = [1, 2, 3, 4];
let b = [3, 4, 5, 6];
print(list_difference(a, b)); // [1, 2]Returns a new list containing all unique elements from both lists.
Example:
let a = [1, 2, 3];
let b = [3, 4, 5];
print(list_union(a, b)); // [1, 2, 3, 4, 5]Splits a list into two lists based on a predicate function.
Returns [truthy_list, falsy_list] where:
-
truthy_listcontains items for whichpredicate(item)is truthy -
falsy_listcontains items for whichpredicate(item)is falsy
Example:
fn is_even(x) { return x % 2 == 0; }
let numbers = [1, 2, 3, 4, 5, 6];
let parts = list_partition(numbers, is_even);
print(parts[0]); // [2, 4, 6] - even numbers
print(parts[1]); // [1, 3, 5] - odd numbersReturns a new list containing the first n elements.
Example:
let items = [1, 2, 3, 4, 5];
print(take(items, 3)); // [1, 2, 3]
print(take(items, 10)); // [1, 2, 3, 4, 5] - takes all availableReturns a new list with the first n elements removed.
Example:
let items = [1, 2, 3, 4, 5];
print(drop(items, 2)); // [3, 4, 5]
print(drop(items, 10)); // [] - drops allReturns a random element from the list, or nil if the list is empty.
Example:
let prizes = ["Car", "TV", "Hat"];
let winner = random_choice(prizes);
print("You won: " + winner);Randomly reorders the elements of the list in place using Fisher-Yates shuffle.
Example:
let deck = [1, 2, 3, 4, 5];
shuffle(deck);
print(deck); // e.g., [3, 1, 5, 2, 4]Returns a random integer between min and max (inclusive).
Example:
let dice_roll = random_int(1, 6);
print("You rolled: " + to_str(dice_roll));Functions for formatting strings with padding and alignment.
Pads the string on the left with spaces (or a custom fill character) to reach the desired width.
- If
widthis less than or equal to the string length, returns the original string unchanged. - Optional third parameter specifies the fill character (default is space
" ").
Example:
print(str_pad_left("42", 5)); // " 42"
print(str_pad_left("42", 5, "0")); // "00042"
print(str_pad_left("hello", 3)); // "hello" (no padding)Pads the string on the right with spaces (or a custom fill character) to reach the desired width.
- If
widthis less than or equal to the string length, returns the original string unchanged. - Optional third parameter specifies the fill character (default is space
" ").
Example:
print(str_pad_right("42", 5)); // "42 "
print(str_pad_right("42", 5, "-")); // "42---"
print(str_pad_right("hello", 3)); // "hello" (no padding)Removes leading and trailing whitespace.
Removes leading whitespace only.
Removes trailing whitespace only.
Converts string to lowercase (ASCII only).
Converts string to uppercase (ASCII only).
Returns true if string starts with the given prefix.
Returns true if string ends with the given suffix.
Returns a new string with s repeated count times.
Splits the string on \n or \r\n. An empty trailing line is preserved.
Pads the string on the left with spaces (or a custom fill character) to reach the desired width.
- If
widthis less than or equal to the string length, returns the original string unchanged. - Optional third parameter specifies the fill character (default is space
" ").
Example:
print(str_pad_left("42", 5)); // " 42"
print(str_pad_left("42", 5, "0")); // "00042"
print(str_pad_left("hello", 3)); // "hello" (no padding)Pads the string on the right with spaces (or a custom fill character) to reach the desired width.
- If
widthis less than or equal to the string length, returns the original string unchanged. - Optional third parameter specifies the fill character (default is space
" ").
Example:
print(str_pad_right("42", 5)); // "42 "
print(str_pad_right("42", 5, "-")); // "42---"
print(str_pad_right("hello", 3)); // "hello" (no padding)Example:
let text = " Hello World ";
print(str_trim(text)); // "Hello World"
print(str_lower("HELLO")); // "hello"
print(str_upper("hello")); // "HELLO"
print(str_startswith("test", "te")); // true
print(str_endswith("test", "st")); // true
print(str_repeat("ab", 3)); // "ababab"
print(split_lines("a\n b\r\n")); // ["a", " b", ""]Joins with / when needed.
Returns directory portion:
- no slash →
.
Returns the current Unix time in milliseconds.
Returns the current Unix time in seconds.
Returns local time as a map with fields:
-
year,month,day -
hour,minute,second,ms -
wday(0=Sunday),yday(1-366) -
is_dst(bool),is_utc(bool)
Same as datetime_now() but in UTC (is_utc = true).
Converts a Unix milliseconds timestamp to local time.
Converts a Unix milliseconds timestamp to UTC.
Example:
let ms = unix_ms();
let local = datetime_now();
let utc = datetime_utc();
let epoch = datetime_from_unix_ms_utc(0);
print(ms, local, utc, epoch);-
/x→/ -
a/b→a
Returns last path component.
Returns extension without dot, or "" if none.
Reads a file and returns its contents as a string. Returns nil if the file can't be read.
Reads a file and returns its raw contents as bytes. Returns nil if the file can't be read.
Writes data to path (overwrites). Returns true on success.
Writes raw bytes to path (overwrites). Returns true on success.
Returns true if the path exists.
Returns true if the path exists and is a directory.
Returns true if the path exists and is a regular file.
Returns a list of entry names (not full paths). Returns nil if the directory can't be opened.
Creates a directory. Returns true on success (or if it already exists).
Removes a file or an empty directory. Returns true on success.
Renames or moves a file or directory. Returns true on success.
Returns the VM's current working directory (used for resolving relative paths).
Sets the VM's current working directory for future relative paths. Returns true on success.
Note: These features are available on Linux only. See File-Handling for detailed documentation and examples.
Matches files using glob patterns with wildcards.
Supported patterns:
-
*- Match any characters except/ -
?- Match single character -
**- Match recursively -
[abc]- Match any char in set -
{a,b,c}- Match any alternative
Example:
// Find all text files
let txt_files = glob("*.txt");
// Find all CupidScript files recursively
let cs_files = glob("**/*.cs", "src");
// Find test files
let tests = glob("test_*.cs", "tests");Monitor files and directories for changes using inotify.
Watch a file for changes. Returns watch handle.
Callback signature: fn(event_type: string, path: string)
Event types: "created", "modified", "deleted", "renamed"
Watch a directory for changes. Returns watch handle.
Set recursive to true to watch subdirectories.
Stop watching a file or directory. Returns true on success.
Process pending file system events. Call this periodically to trigger callbacks.
Example:
// Watch configuration file
let handle = watch_file("config.yaml", fn(event, path) {
print("Config changed:", event);
});
// Main loop
while running {
process_file_watches();
sleep(100);
}
unwatch(handle);Create temporary files and directories with automatic cleanup on exit.
Create a temporary file. Returns path to file.
Example:
let temp = temp_file("myapp_", ".txt");
write_file(temp, "temporary data");
// Automatically cleaned up on exitCreate a temporary directory. Returns path to directory.
Example:
let workspace = temp_dir("build_");
mkdir(workspace + "/obj");
write_file(workspace + "/obj/main.o", "...");
// Entire tree cleaned up on exitCreate and extract tar and gzip archives.
Compress a file using gzip. Returns true on success.
Decompress a gzip file. Returns true on success.
Example:
gzip_compress("large.txt", "large.txt.gz");
gzip_decompress("large.txt.gz", "restored.txt");Create a tar archive from a list of files.
Set compress to "gzip" to create a .tar.gz file.
List contents of a tar archive. Returns list of filenames or nil on error.
Extract a tar archive to a directory. Returns true on success.
Example:
// Create tar archive
let files = ["file1.txt", "file2.txt", "file3.cs"];
tar_create("backup.tar", files);
// List contents
let contents = tar_list("backup.tar");
// Extract
tar_extract("backup.tar", "restored");
// Create compressed archive
tar_create("backup.tar.gz", files, "gzip");For comprehensive documentation, examples, and best practices, see File-Handling.
Supported specifiers:
-
%dexpects int -
%bexpects bool -
%sexpects string -
%vaccepts any value (uses a generic repr)
Literal percent: %%
Example:
print(fmt("x=%d ok=%b name=%s", 10, true, "frank"));Parses JSON into CupidScript values, fully conforming to RFC 8259:
-
null→nil -
true/false→bool - numbers →
intorfloat - strings →
string(all Unicode escapes, including surrogate pairs, are decoded to UTF-8) - arrays →
list - objects →
map(object keys must be strings; non-string keys are rejected)
All Unicode escapes (\uXXXX) are decoded, including surrogate pairs for code points above U+FFFF. Invalid or incomplete surrogate pairs are rejected. CupidScript strings are always valid UTF-8.
Object keys must be strings. Parsing or stringifying a map with non-string keys will result in an error, as required by RFC 8259.
Serializes a value to JSON. Supports nil, bool, int, float, string, list, map (with string keys only). Produces strict RFC 8259-compliant JSON.
Example:
let obj = {"name": "frank", "age": 30, "tags": ["a", "b"]};
let s = json_stringify(obj);
let back = json_parse(s);
print(back.name);See also: JSON Unicode and Surrogate Example
CupidScript supports CSV, YAML, and XML data formats. See Data-Formats for detailed usage and examples.
Parses CSV text into a list of lists (rows). Each row is a list of string fields.
Options:
-
delimiter- Field separator (default:",") -
quote- Quote character (default:"\"") -
headers- Treat first row as headers, return list of maps (default:false) -
skip_empty- Skip empty rows (default:false) -
trim- Trim whitespace from fields (default:false)
Example:
let csv = "name,age,city\nJohn,30,NYC\nAlice,25,LA";
let rows = csv_parse(csv);
// Returns: [["name", "age", "city"], ["John", "30", "NYC"], ["Alice", "25", "LA"]]
// With headers
let records = csv_parse(csv, {headers: true});
// Returns: [{"name": "John", "age": "30", "city": "NYC"}, ...]Converts a list of lists (or list of maps) to CSV format.
Options:
-
delimiter- Field separator (default:",") -
quote- Quote character (default:"\"")
Example:
let data = [["name", "age"], ["Bob", "35"]];
let csv = csv_stringify(data);
// Returns: "name,age\nBob,35\n"Parses YAML 1.2.2 compliant text into CupidScript values.
Parameters:
-
text(string) - YAML-formatted text to parse
Returns:
- Parsed value (map, list, string, number, boolean, or nil)
-
nilon parse error (error message is set)
Supported Features:
Scalars:
- Plain strings, quoted strings (single
'...'or double"...") - Integers:
123,-456,0xFF(hex),0o777(octal) - Floats:
3.14,-2.5,1e3(scientific),.inf,-.inf,.nan - Booleans:
true,false,yes,no,on,off - Null:
null,~, or empty value
Collections:
- Maps (key-value): block or flow style
{key: value} - Lists: block or flow style
[item1, item2]
Advanced Features:
-
Block scalars:
|(literal),>(folded) -
Chomping:
|-(strip),|+(keep),|(clip) -
Explicit indentation:
|2,>4 -
Type tags:
!!str,!!int,!!float,!!bool,!!null -
Anchors/aliases:
&anchor,*alias -
Merge keys:
<<: *anchor -
Explicit keys:
? key -
Multi-document:
---separator -
Directives:
%YAML 1.2,%TAG
Escape Sequences (double-quoted strings only):
-
Common:
\\,\",\0,\t,\n,\r,\a,\b,\v,\f,\e,\ -
Hex:
\xHH(2 hex digits, e.g.,\x41→ A) -
Unicode:
\uXXXX(4 hex digits),\UXXXXXXXX(8 hex digits) -
Special (YAML 1.2.2):
\N(next line),\_(NBSP),\L(line separator),\P(paragraph separator)
Error Conditions:
- Invalid indentation (tabs in block scalars, inconsistent spacing)
- Reserved indicators
@or`starting plain scalars - Unterminated quoted strings
- Invalid escape sequences
- Undefined aliases
- Malformed tags
- Syntax errors in flow collections
Examples:
// Basic parsing
let yaml = "name: MyApp\nversion: 1.0.0\ndebug: true";
let config = yaml_parse(yaml);
print(config.name); // "MyApp"
print(config.debug); // true
// Escape sequences
let escaped = yaml_parse("text: \"Line1\\nLine2\\x20Tab\\there\"\n");
print(escaped.text); // "Line1\nLine2 Tab\there"
// Advanced features
let advanced = yaml_parse("
text: |-
no trailing newlines
value: !!str 123
base: &base
x: 1
y: 2
derived:
<<: *base
y: 99
");
print(advanced.derived.x); // 1 (from base)
print(advanced.derived.y); // 99 (overridden)
// Error handling
let bad = yaml_parse("invalid: [unclosed");
if (bad == nil) {
print("Parse error occurred");
}Performance:
- Optimized for config files up to 1MB
- Large files (> 10MB) may be slow
- Deep nesting (> 100 levels) supported but impacts performance
See Also: Data-Formats for comprehensive YAML guide
Parses multiple YAML documents separated by --- from a single string.
Parameters:
-
text(string) - Multi-document YAML text
Returns:
- List of parsed documents
- Empty list
[]if input contains no documents -
nilon parse error
Example:
let docs = yaml_parse_all("
---
name: Config
version: 1.0
...
---
name: Data
items: [a, b, c]
---
name: Metadata
");
print(len(docs)); // 3
print(docs[0].name); // "Config"
print(docs[1].name); // "Data"
print(docs[2].name); // "Metadata"
// Single document also works
let single = yaml_parse_all("key: value");
print(len(single)); // 1
print(single[0].key); // "value"Note: The explicit document end marker ... is optional.
Converts a CupidScript value to YAML 1.2.2 format.
Parameters:
-
value(any) - Value to serialize (map, list, string, number, boolean, nil) -
indent(int, optional) - Indentation spaces (default: 2, range: 0-8)
Returns:
- YAML-formatted string in block style
Serialization Rules:
- Maps → YAML mappings (key: value)
- Lists → YAML sequences (- item)
- Strings → plain or quoted (auto-detected)
- Numbers → numeric scalars
- Booleans →
trueorfalse -
nil→null - Special characters in strings trigger quoting
- Reserved indicators
@and`are quoted
Example:
let data = {"host": "localhost", "port": 8080, "tags": ["a", "b"]};
let yaml = yaml_stringify(data);
// Returns block-style YAML:
// host: localhost
// port: 8080
// tags:
// - a
// - b
// Custom indentation
let yaml4 = yaml_stringify(data, 4);
// host: localhost
// port: 8080
// tags:
// - a
// - b
// Handles special characters
let special = {"path": "C:\\Users", "at": "@value"};
let yaml_special = yaml_stringify(special);
// path: "C:\\Users"
// at: "@value"Limitations:
- Does not generate anchors/aliases (use explicit YAML)
- Does not generate tags (all values use implicit typing)
- Always uses block style (not flow style)
See Also: Data-Formats for YAML details
Parses XML into a map structure:
{
"name": "element-name",
"attrs": {"attr1": "value1", ...}, // Optional
"text": "text content", // Optional
"children": [...] // Optional
}Features:
- Elements, attributes, text content
- Self-closing tags:
<br/> - CDATA sections
- Entity decoding:
<,>,&,",' - Numeric entities:
A,A - Comments (skipped)
- Processing instructions (skipped)
Example:
let xml = "<person name=\"John\" age=\"30\"><city>NYC</city></person>";
let doc = xml_parse(xml);
print(doc.name); // "person"
print(doc.attrs.name); // "John"
print(doc.children[0].text); // "NYC"Converts an element map to XML format.
Parameters:
-
element- Map withname, optionalattrs,text, andchildren -
indent- Indentation spaces (default: 2, range: 0-8, 0 = compact)
Example:
let elem = {
"name": "book",
"attrs": {"isbn": "123"},
"children": [
{"name": "title", "text": "Guide"},
{"name": "author", "text": "John"}
]
};
let xml = xml_stringify(elem);
// Returns:
// <book isbn="123">
// <title>Guide</title>
// <author>John</author>
// </book>Milliseconds timestamp:
- Uses
gettimeofdayon POSIX - Uses
clock()fallback on Windows build
Returns a promise that resolves after ms milliseconds.
await sleep(10);If ms <= 0, the promise resolves immediately.
event_loop_start() -> bool — Start the background event loop (returns true on success).
event_loop_stop() -> bool — Stop and join the background event loop.
event_loop_running() -> bool — Check if the background event loop is running.
Note: This is a Linux-only feature that runs the VM scheduler and pending I/O on a separate thread so the main thread can block on await while the event loop makes progress.
Creates a new pending promise.
Resolves a promise. Returns true if it was pending.
Rejects a promise. Returns true if it was pending.
Checks if a value is a promise.
CupidScript provides a comprehensive set of mathematical functions for scientific computing, game development, and general numeric operations.
Returns absolute value. Preserves type (int → int, float → float).
abs(-5) // 5
abs(-3.14) // 3.14
abs(42) // 42Returns minimum of one or more values. Accepts any number of int and/or float arguments.
min(3, 7, 2) // 2
min(1.5, 2.5) // 1.5
min(-10, 0, 10) // -10Returns maximum of one or more values. Accepts any number of int and/or float arguments.
max(3, 7, 2) // 7
max(1.5, 2.5) // 2.5
max(-10, 0, 10) // 10Constrains a value to the range [min, max]. Returns int if all arguments are int, otherwise float.
clamp(5, 0, 10) // 5
clamp(-5, 0, 10) // 0
clamp(15, 0, 10) // 10
clamp(3.7, 0, 5) // 3.7Rounds down to nearest integer. Accepts int or float, always returns int.
floor(3.7) // 3
floor(-3.7) // -4
floor(5) // 5Rounds up to nearest integer. Accepts int or float, always returns int.
ceil(3.2) // 4
ceil(-3.2) // -3
ceil(5) // 5Rounds to nearest integer (0.5 rounds away from zero). Returns int.
round(3.7) // 4
round(3.2) // 3
round(3.5) // 4
round(-3.5) // -4Truncates decimal part (rounds toward zero). Returns int.
trunc(3.7) // 3
trunc(-3.7) // -3
trunc(5) // 5Returns square root. Accepts int or float, always returns float.
sqrt(16) // 4.0
sqrt(2) // 1.414...
sqrt(0) // 0.0Returns base raised to exp power. Accepts int or float, returns float.
pow(2, 8) // 256.0
pow(10, 3) // 1000.0
pow(2, -1) // 0.5
pow(4, 0.5) // 2.0 (square root)Returns
exp(0) // 1.0
exp(1) // 2.71828... (E)
exp(2) // 7.389...Returns natural logarithm of x (base
log(E) // 1.0
log(1) // 0.0
log(10) // 2.302...Returns base-10 logarithm of x.
log10(100) // 2.0
log10(1000) // 3.0
log10(1) // 0.0Returns base-2 logarithm of x.
log2(8) // 3.0
log2(1024) // 10.0
log2(1) // 0.0All trigonometric functions use radians. Use PI constant for conversions.
Returns sine of x (radians).
sin(0) // 0.0
sin(PI / 2) // 1.0
sin(PI) // ~0.0Returns cosine of x (radians).
cos(0) // 1.0
cos(PI / 2) // ~0.0
cos(PI) // -1.0Returns tangent of x (radians).
tan(0) // 0.0
tan(PI / 4) // 1.0Returns arc sine of x (result in radians). Input must be in range [-1, 1].
asin(0) // 0.0
asin(1) // 1.5707... (PI/2)
asin(0.5) // 0.5235... (PI/6)Returns arc cosine of x (result in radians). Input must be in range [-1, 1].
acos(1) // 0.0
acos(0) // 1.5707... (PI/2)
acos(-1) // 3.1415... (PI)Returns arc tangent of x (result in radians).
atan(0) // 0.0
atan(1) // 0.7853... (PI/4)
atan(-1) // -0.7853... (-PI/4)Returns arc tangent of y/x using signs of both arguments to determine quadrant (result in radians, range [-π, π]).
atan2(1, 1) // 0.7853... (PI/4, 45°)
atan2(1, 0) // 1.5707... (PI/2, 90°)
atan2(0, -1) // 3.1415... (PI, 180°)
atan2(-1, 0) // -1.5707... (-PI/2, -90°)Use Case: Converting Cartesian coordinates to polar angle:
let angle = atan2(y, x); // Correctly handles all quadrantsAll random functions automatically seed on first use.
Returns a pseudo-random float in the range [0.0, 1.0).
let r = random(); // e.g., 0.7234...
let percent = random() * 100; // 0 to 100Returns a random integer in the range [min, max] (inclusive).
random_int(1, 6) // Dice roll: 1, 2, 3, 4, 5, or 6
random_int(0, 100) // Random percentage
random_int(-10, 10) // Random value from -10 to 10Note: Arguments can be in any order (automatically swapped if needed).
Returns a random element from a list. Returns nil if list is empty.
let colors = ["red", "green", "blue"];
let color = random_choice(colors); // e.g., "green"
let options = [1, 2, 3, 4, 5];
let pick = random_choice(options); // e.g., 3Randomly shuffles a list in-place using Fisher-Yates algorithm. Returns nil.
let deck = [1, 2, 3, 4, 5];
shuffle(deck);
print(deck); // e.g., [3, 1, 5, 2, 4]Use Case: Randomize order for games, simulations, or sampling.
// Degrees to radians
fn deg_to_rad(deg) { return deg * PI / 180; }
// Radians to degrees
fn rad_to_deg(rad) { return rad * 180 / PI; }
// Examples
let angle_rad = deg_to_rad(90); // PI/2
let angle_deg = rad_to_deg(PI); // 180The constant π ≈ 3.14159265358979323846.
let circumference = 2 * PI * radius;
let area = PI * radius * radius;Euler's number
let growth = E;
let decay = exp(-t / tau);// Calculate distance between two points
fn distance(x1, y1, x2, y2) {
let dx = x2 - x1;
let dy = y2 - y1;
return sqrt(dx * dx + dy * dy);
}
// Clamp value with bounds checking
fn safe_clamp(val, minimum, maximum) {
return clamp(val, min(minimum, maximum), max(minimum, maximum));
}
// Random point on unit circle
fn random_unit_circle() {
let angle = random() * 2 * PI;
return {x: cos(angle), y: sin(angle)};
}
// Test math functions
print("Distance:", distance(0, 0, 3, 4)); // 5.0
print("Clamped:", safe_clamp(15, 0, 10)); // 10
let point = random_unit_circle();
print("Random point:", point.x, point.y);Converts a value to integer:
-
int→ returns as-is -
float→ truncates toward zero (3.9 → 3, -3.9 → -3) -
bool→0or1 -
string→ parses decimal integer, returnsnilif invalid - Otherwise →
nil
Converts any value to its string representation.
Manually triggers garbage collection cycle detection. Returns the number of cycles collected.
Returns GC statistics as a map:
-
tracked- Number of tracked objects (lists/maps/sets/tuples) -
collections- Total collections performed -
collected- Total objects collected -
allocations- Total allocations since VM start
Returns current GC configuration:
-
threshold- Collection threshold (0 = disabled) -
alloc_trigger- Allocation trigger interval (0 = disabled)
Sets GC configuration from a map with optional fields:
-
threshold(int) -
alloc_trigger(int)
Returns true.
Sets both values directly. Returns true.
Auto-GC Policy:
- GC can run when tracked objects >=
threshold - GC can run every
alloc_triggerallocations - Both policies can be enabled simultaneously
- Setting either value to
0disables that policy
See Implementation Notes for details.
Scripts can configure their own execution limits:
Sets maximum execution time in milliseconds. Script aborts if exceeded.
set_timeout(30000); // Allow 30 seconds for batch processingSets maximum instruction count. Script aborts if exceeded.
set_instruction_limit(100000000); // Allow 100M instructionsReturns current timeout in milliseconds (0 = unlimited).
Returns current instruction limit (0 = unlimited).
Returns number of instructions executed in current script run.
Example:
// Check if we need more time for heavy processing
if (get_instruction_count() > get_instruction_limit() / 2) {
print("Warning: approaching instruction limit");
set_instruction_limit(get_instruction_limit() * 2);
}// Connect to a server
let sock = await tcp_connect("example.com", 80);
// Send data
await socket_send(sock, "GET / HTTP/1.1\r\nHost: example.com\r\n\r\n");
// Receive data
let response = await socket_recv(sock, 4096);
// Close socket
socket_close(sock);let server = tcp_listen("0.0.0.0", 8080);
print("Listening on port 8080...");
let client = await socket_accept(server);
let data = await socket_recv(client, 1024);
await socket_send(client, "HTTP/1.1 200 OK\r\n\r\nHello!");
socket_close(client);
socket_close(server);Note: TLS support requires building without
CS_NO_TLSflag and linking against OpenSSL/LibreSSL.
// Connect with TLS (requires TLS support compiled in)
let sock = await tls_connect("example.com", 443);
// Check if secure
print(socket_is_secure(sock)); // true
// Get TLS info
let info = tls_info(sock);
print(info.version); // "TLSv1.3"
print(info.cipher); // "TLS_AES_256_GCM_SHA384"
// Upgrade existing connection (STARTTLS)
let smtp = await tcp_connect("smtp.example.com", 587);
await socket_send(smtp, "STARTTLS\r\n");
await tls_upgrade(smtp); // Now encrypted
socket_close(sock);// HTTP request
let resp = await http_get("http://example.com");
// HTTPS request (automatic TLS, requires TLS support)
let resp = await http_get("https://api.example.com/data");
print(resp.status); // 200
print(resp.body);
// POST request
let resp = await http_post("https://api.example.com/data", json_stringify({key: "value"}));
// DELETE request
let resp = await http_delete("https://api.example.com/resource/1");
// Full control with http_request
let resp = await http_request({
method: "PUT",
url: "https://api.example.com/users/1",
headers: {
"Content-Type": "application/json",
"Authorization": "Bearer token123"
},
body: json_stringify({name: "bob"}),
timeout: 10000,
follow_redirects: true,
verify_ssl: true // default, set false to skip cert verification
});Note: All HTTP functions are asynchronous and return promises. Use await to get the response.
let parts = url_parse("http://user:pass@example.com:8080/path?q=1");
// {scheme: "http", user: "user", pass: "pass", host: "example.com", port: 8080, path: "/path", query: "q=1"}
let url = url_build({scheme: "https", host: "api.example.com", path: "/v1/users"});
// "https://api.example.com/v1/users"// Set default timeout for network operations (in milliseconds)
net_set_default_timeout(60000); // 60 second default timeoutPlatform Notes:
- Network I/O uses non-blocking sockets with the event loop
- On Linux/Unix: Uses
poll()for multiplexing - On Windows: Uses
select()for multiplexing - TLS support is optional and requires OpenSSL/LibreSSL at build time