# Halts execution with error code 5
def halt_error(): halt(5);
# Checks if input is an array
def is_array(a): type(a) == "array";
# Checks if input is markdown
def is_markdown(m): type(m) == "markdown";
# Checks if input is a boolean
def is_bool(b): type(b) == "bool";
# Checks if input is a number
def is_number(n): type(n) == "number";
# Checks if input is a string
def is_string(s): type(s) == "string";
# Checks if input is None
def is_none(n): type(n) == "None";
# Checks if input is a dictionary
def is_dict(d): type(d) == "dict";
# Checks if string contains a substring
def contains(haystack, needle):
if (is_dict(haystack)):
!is_none(haystack[needle])
else:
index(haystack, needle) != -1
end
# Removes prefix string from input if it exists
def ltrimstr(s, left):
if (starts_with(s, left)):
slice(s, index(s, left) + len(left), len(s))
else:
s
end
# Removes suffix string from input if it exists
def rtrimstr(s, right):
if (ends_with(s, right)):
slice(s, 0, sub(len(s), len(right)))
else:
s
end
# Checks if string, array or dict is empty
def is_empty(s):
if (is_string(s) || is_array(s) || is_dict(s)):
len(s) == 0
elif (is_none(s)):
true
else:
false
end
# Tests if string matches a pattern
def test(s, pattern): is_regex_match(s, pattern);
# Returns value if condition is true, None otherwise
def select(v, f): if (f): v;
# Returns array if input is array, None otherwise
def arrays(a): select(a, is_array(a));
# Returns markdown if input is markdown, None otherwise
def markdowns(m): select(m, is_markdown(m));
# Returns boolean if input is boolean, None otherwise
def booleans(b): select(b, is_bool(b));
# Returns number if input is number, None otherwise
def numbers(n): select(n, is_number(n));
# Formats a date to ISO 8601 format (YYYY-MM-DDTHH:MM:SSZ)
def to_date_iso8601(d): to_date(d, "%Y-%m-%dT%H:%M:%SZ");
# Applies a given function to each element of the provided array and returns a new array with the results.
def map(v, f):
if (is_dict(v)):
do
let map_dict = fn(v, f): let mapped = foreach (x, entries(v)): f(x); | dict(mapped);
| map_dict(v, f)
end
elif (is_none(v)):
None
else:
foreach (x, v): f(x);
end
# Applies a function to each element and flattens the result into a single array
def flat_map(v, f):
if (is_none(v)):
None
else:
foreach (x, v): f(x); | flatten()
end
# Filters the elements of an array based on a provided callback function.
def filter(v, f):
if (is_dict(v)):
do
let filter_dict = fn(v, f): let fileted = foreach (x, entries(v)): select(x, f(x)); | dict(compact(fileted));
| filter_dict(v, f)
end
elif (is_none(v)):
None
else:
do
let _filter = fn(v, f): foreach (x, v): select(x, f(x)); | compact();
| _filter(v, f)
end
end
# Executes a provided function once for each element in an array or each key-value pair in a dictionary.
def each(v, f):
let target = if (is_dict(v)): entries(v) else: v
| var i = 0
| loop:
if (i >= len(target)): break
| f(target[i])
| i += 1
| None
end
| v
end
# Returns the first element of an array
def first(arr): arr[0];
# Returns the last element of an array
def last(arr): arr[len(arr) - 1];
# Returns the second element of an array
def second(arr): if (len(arr) > 1): arr[1];
# Checks if markdown is h1 heading
def is_h1(md): to_md_name(md) == "h1";
# Checks if markdown is h2 heading
def is_h2(md): to_md_name(md) == "h2";
# Checks if markdown is h3 heading
def is_h3(md): to_md_name(md) == "h3";
# Checks if markdown is h4 heading
def is_h4(md): to_md_name(md) == "h4";
# Checks if markdown is h5 heading
def is_h5(md): to_md_name(md) == "h5";
# Checks if markdown is h6 heading
def is_h6(md): to_md_name(md) == "h6";
# Checks if markdown is heading
def is_h(md): or(is_h1(md), is_h2(md), is_h3(md), is_h4(md), is_h5(md), is_h6(md));
# Checks if markdown is a heading of the specified level (1-6)
def is_h_level(md, level):
if (level == 1):
is_h1(md)
elif (level == 2):
is_h2(md)
elif (level == 3):
is_h3(md)
elif (level == 4):
is_h4(md)
elif (level == 5):
is_h5(md)
elif (level == 6):
is_h6(md)
else:
error("Invalid heading level: " + to_string(level))
end
# Checks if markdown is table align
def is_table_align(md): to_md_name(md) == "table_align";
# Checks if markdown is table cell
def is_table_cell(md): to_md_name(md) == "table_cell";
# Checks if markdown is emphasis
def is_em(md): to_md_name(md) == "emphasis";
# Checks if markdown is html
def is_html(md): to_md_name(md) == "html";
# Checks if markdown is yaml
def is_yaml(md): to_md_name(md) == "yaml";
# Checks if markdown is toml
def is_toml(md): to_md_name(md) == "toml";
# Checks if markdown is code block
def is_code(md): to_md_name(md) == "code";
# Checks if markdown is text
def is_text(text): to_md_name(text) == "text";
# Checks if markdown is list
def is_list(list): to_md_name(list) == "list";
# Checks if markdown node's URL matches a specified URL
# deprecated: use select(.link.url == url) instead
def matches_url(node, url): get_url(node) == url;
# Checks if markdown is MDX Flow Expression
def is_mdx_flow_expression(mdx): to_md_name(mdx) == "mdx_flow_expression";
# Checks if markdown is MDX Jsx Flow Element
def is_mdx_jsx_flow_element(mdx): to_md_name(mdx) == "mdx_jsx_flow_element";
# Checks if markdown is MDX Jsx Text Element
def is_mdx_jsx_text_element(mdx): to_md_name(mdx) == "mdx_jsx_text_element";
# Checks if markdown is MDX Text Expression
def is_mdx_text_expression(mdx): to_md_name(mdx) == "mdx_text_expression";
# Checks if markdown is MDX Js Esm
def is_mdx_js_esm(mdx): to_md_name(mdx) == "mdx_js_esm";
# Checks if markdown is MDX
def is_mdx(mdx):
is_mdx_flow_expression(mdx) ||
is_mdx_jsx_flow_element(mdx) ||
is_mdx_jsx_text_element(mdx) ||
is_mdx_text_expression(mdx) ||
is_mdx_js_esm(mdx)
end
# Returns an array of length n filled with the given value.
def fill(value, n):
if (n < 0):
error("n must be non-negative")
elif (n == 0):
[]
else:
foreach (i, range(0, n - 1)): value;
end
# Sorts an array using a key function that extracts a comparable value for each element.
def sort_by(arr, f):
let decorate_arr = foreach (x, arr): [f(x), x]; | _sort_by_impl(decorate_arr) | map(second);
# Returns the count of elements in the array that satisfy the provided function.
def count_by(arr, f):
if (!is_array(arr) || is_empty(arr)):
0
else:
filter(arr, f) | len()
end
# Skips the first n elements of an array and returns the rest
def skip(arr, n):
if (n < 0):
error("n must be non-negative")
elif (n > len(arr)): []
else:
slice(arr, n, len(arr))
end
# Takes the first n elements of an array
def take(arr, n):
if (n < 0):
error("n must be non-negative")
elif (n > len(arr)):
arr
else:
slice(arr, 0, n)
end
# Returns the index of the first element in an array that satisfies the provided function.
def find_index(arr, f):
let _find_index_impl = fn(arr, f, idx):
if (idx == len(arr)):
-1
elif (f(get(arr, idx))):
idx
else:
_find_index_impl(arr, f, idx + 1);
| if (not(is_array(arr))):
error("first argument must be an array")
else:
_find_index_impl(arr, f, 0)
end
# Skips elements from the beginning of an array while the provided function returns true
def skip_while(arr, f):
if (!is_array(arr)):
error("first argument must be an array")
elif (is_empty(arr)):
[]
else:
do
var i = 0
| while (i < len(arr) && f(get(arr, i))):
i += 1
end
| slice(arr, i, len(arr))
end
end
# Takes elements from the beginning of an array while the provided function returns true
def take_while(arr, f):
if (not(is_array(arr))):
error("first argument must be an array")
elif (is_empty(arr)):
[]
else:
do
var i = 0
| while (and(i < len(arr), f(get(arr, i)))):
i += 1
end
| slice(arr, 0, i)
end
end
# Groups elements of an array by the result of applying a function to each element
def group_by(arr, f):
var groups = dict()
| if (not(is_array(arr))):
error("first argument must be an array")
elif (is_empty(arr)):
dict()
else:
do
var i = 0
| while (not(is_none(get(arr, i)))):
let v = get(arr, i)
| i += 1
| let key = to_string(f(v))
| let existing = get(groups, key)
| let new_group = if (is_none(existing)): [v] else: existing + v
| groups = set(groups, key, new_group)
| groups
end
end
end
# Returns true if any element in the array satisfies the provided function.
def any(v, f): if (is_empty(v)): false else: len(filter(v, f)) != 0;
# Returns true if all element in the array satisfies the provided function.
def all(v, f): if (is_empty(v)): true else: len(filter(v, f)) == len(v);
# Returns true if the element is in the array.
def in(v, elem):
if (is_array(v)):
if (is_array(elem)):
all(elem, fn(x): in(v, x);)
else:
any(v, fn(x): x == elem;)
else:
contains(v, elem)
end
# Reduces an array to a single value by applying a function, starting from an initial value.
def fold(arr, init, f):
if (not(is_array(arr))):
error("first argument must be an array")
elif (is_empty(arr)):
init
else:
do
var acc = init
| var i = 0
| while (i != len(arr)):
acc = f(acc, get(arr, i))
| i += 1
| acc
end
end
end
# Returns a new array with duplicate elements removed, comparing by the result of the provided function.
def unique_by(arr, f):
if (not(is_array(arr))):
error("first argument must be an array")
elif (is_empty(arr)): []
else:
do
var seen = dict()
| var result = []
| var i = 0
| while (i != len(arr)):
let item = get(arr, i)
| let key = to_string(f(item))
| let already_seen = get(seen, key)
| result = if (is_none(already_seen)): result + item else: result
| seen = if (is_none(already_seen)): set(seen, key, true) else: seen
| i += 1
| result
end
end
end
# Returns the input value unchanged.
def identity(x): x;
# Transposes a 2D array (matrix), swapping rows and columns.
def transpose(matrix):
if (!is_array(matrix)):
error("transpose: input must be an array of arrays")
elif (is_empty(matrix)): []
elif (!all(matrix, is_array)):
error("transpose: all elements must be arrays")
else: do
let row_count = len(matrix)
| let col_count = if (row_count == 0): 0 else: len(matrix[0])
| foreach (j, range(0, col_count - 1)):
foreach (i, range(0, row_count - 1)):
matrix[i][j]
end
end
end
end
# Applies a function to a value and returns the value (useful for debugging or side effects).
macro tap(tap_value, tap_expr) do
quote do
tap_expr | tap_value
end
end
# Executes the expression only if the condition is false.
macro unless(unless_cond, unless_expr) do
quote do
if (unquote(!unless_cond)): unquote(unless_expr)
end
end
# Executes the expression repeatedly until the condition is true.
macro until(until_cond, until_expr) do
quote do
loop:
if (unquote(until_cond)): break
| unquote(until_expr)
end
end
end
# Extracts values from an array of objects based on a specified selector.
macro pluck(pluck_obj, selector) do
quote do
match (unquote(pluck_obj)):
| :array: do
unquote(pluck_obj)
| map(fn(x):
match (x):
| :dict: x[unquote(selector)]
| _: do
x | select(unquote(selector), unquote(selector))
end
end
end) | compact()
end
| _: do
unquote(pluck_obj) | unquote(selector)
end
end
end
end
# Maps over an array and removes None values from the result.
def compact_map(arr, f): map(arr, f) | compact();
# Filters out elements that match the condition (opposite of filter).
def reject(arr, f): filter(arr, fn(x): !f(x););
# Splits an array into two arrays: [matching, not_matching] based on a condition.
def partition(arr, f): [filter(arr, f), filter(arr, fn(x): !f(x);)];
# Safely gets a value from a dict with a default if the key doesn't exist.
def get_or(dict, key, default):
let val = get(dict, key)
| if (is_none(val)): default else: val
end
# Executes an expression n times and returns an array of results.
macro times(t_n, t_expr) do
quote do
if (unquote(t_n) == 0):
[]
else:
foreach (_, range(0, unquote(t_n) - 1)): unquote(t_expr);
end
end
# Checks if a value is between min and max (inclusive).
def between(value, min, max): value >= min && value <= max;
# Sums elements of an array after applying a transformation function.
def sum_by(arr, f): fold(arr, 0, fn(acc, x): acc + f(x););
# Creates a dictionary indexed by a key extracted from each element.
def index_by(arr, f):
fold(arr, dict(), fn(acc, item):
let key = to_string(f(item))
| let existing = get(acc, key)
| let new_value = if (is_none(existing)): item else: existing + item
| set(acc, key, new_value);
)
end
# Inspects a value by printing its string representation and returning the value.
def inspect(value) do
print(to_string(value))
| value
end
# Left-pads a string to a specified length using a given padding string.
def lpad(s, length, pad_str = " "):
let pad_needed = length - len(s)
| if (pad_needed <= 0):
s
else: do
let full_repeats = floor(pad_needed / len(pad_str))
| let remainder = pad_needed % len(pad_str)
| let full_pad = do foreach (_, range(0, full_repeats - 1)): pad_str; | join("");
| let final_pad = full_pad + slice(pad_str, 0, remainder)
| final_pad + s
end
end
# Right-pads a string to a specified length using a given padding string.
def rpad(s, length, pad_str = " "):
let pad_needed = length - len(s)
| if (pad_needed <= 0):
s
else: do
let full_repeats = floor(pad_needed / len(pad_str))
| let remainder = pad_needed % len(pad_str)
| let full_pad = do foreach (_, range(0, full_repeats - 1)): pad_str; | join("");
| let final_pad = full_pad + slice(pad_str, 0, remainder)
| s + final_pad
end
end
# Loads a markdown file from the specified path
def load_markdown(path): read_file(path) | to_markdown();
# Prints the debug information of the given value(s).
def debug(*args):
if (len(args) == 1):
stderr(s"DEBUG: ${args[0]}")
else:
stderr("DEBUG:\n" + do args | map(fn(arg): s" - ${arg}";) | join("\n");)
end
# Increases the depth (numeric level) of a markdown heading node by one,
# effectively demoting the heading (e.g. h1 -> h2), up to a maximum of 6.
def increase_header_depth(node):
shift_right(node, 1)
end
# Decreases the depth (numeric level) of a markdown heading node by one,
# effectively promoting the heading (e.g. h2 -> h1), down to a minimum of 1.
def decrease_header_depth(node):
shift_left(node, 1)
end
# Demotes a markdown heading by increasing its depth (numeric level) by one.
# This is an alias for `increase_header_depth`.
def demote_heading(node):
increase_header_depth(node)
end
# Promotes a markdown heading by decreasing its depth (numeric level) by one.
# This is an alias for `decrease_header_depth`.
def promote_heading(node):
decrease_header_depth(node)
end
# Deprecated: use `increase_header_depth` or `demote_heading` instead.
# Kept for backward compatibility; behavior unchanged.
def increase_header_level(node):
increase_header_depth(node)
end
# Deprecated: use `decrease_header_depth` or `promote_heading` instead.
# Kept for backward compatibility; behavior unchanged.
def decrease_header_level(node):
decrease_header_depth(node)
end
# Performs a binary search on a sorted array to find the index of the target value.
def bsearch(arr, target):
if (not(is_array(arr))):
error("first argument must be an array")
else: do
let _bsearch_impl = fn(arr, target, low, high):
if (low > high):
-1
else:
do
let mid = floor((low + high) / 2)
| let mid_val = arr[mid]
| if (mid_val == target):
mid
elif (mid_val < target):
_bsearch_impl(arr, target, mid + 1, high)
else:
_bsearch_impl(arr, target, low, mid - 1);
end
| _bsearch_impl(arr, target, 0, len(arr) - 1)
end
end
# Converts a string into a URL-friendly slug by lowercasing, replacing non-alphanumeric characters with hyphens, and trimming hyphens from the ends.
def slugify(s, separator = "-"):
s
| downcase()
| gsub("[^a-z0-9]+", separator)
| gsub("^-+|-+$", "")
end
# Calculates the p-th percentile of an array of numbers using linear interpolation between closest ranks.
def percentile(arr, p):
if (not(is_array(arr))):
error("first argument must be an array")
elif (is_empty(arr)):
None
elif (p < 0 || p > 1):
error("p must be between 0 and 1")
else:
do
let sorted = sort(arr)
| let rank = p * (len(sorted) - 1)
| let lower_index = floor(rank)
| let upper_index = ceil(rank)
| let weight = rank - lower_index
| if (upper_index >= len(sorted)):
sorted[lower_index]
else:
sorted[lower_index] * (1 - weight) + sorted[upper_index] * weight
end
end