In a YAML config, each placeholder value is an expression that gets evaluated
automatically. Inside double quotes, you can embed {expression} for interpolation.
| Type | Example | Description |
|---|---|---|
| Number | 42, 3.14 |
Integer and floating-point numbers |
| String | 'hello', "world" |
Text in single or double quotes |
| Operator | Description | Example | Result |
|---|---|---|---|
+ |
Addition | 10 + 3 |
13 |
- |
Subtraction | 10 - 3 |
7 |
* |
Multiplication | 4 * 5 |
20 |
/ |
Division | 10 / 3 |
3.333... |
% |
Modulo | 10 % 3 |
1 |
- |
Unary minus | -5 |
-5 |
Precedence: *, /, % are evaluated before +, -. Parentheses () change order.
Return True / False. Commonly used together with IF().
| Operator | Description | Example | Result |
|---|---|---|---|
> |
Greater than | 5 > 3 |
True |
< |
Less than | 2 < 1 |
False |
>= |
Greater or equal | 10 >= 10 |
True |
<= |
Less or equal | 5 <= 4 |
False |
== |
Equal | 3 == 3 |
True |
!= |
Not equal | 3 != 4 |
True |
Inside a double-quoted string, {expression} is replaced with the evaluated value:
DESCRIPTION:
"Invoice #{INVOICE_NUM} dated {CURRENT_DATE_STR(month)} {CURRENT_DATE_NUM(day)}"Converts text to uppercase.
TITLE: UPPER('hello world') # → "HELLO WORLD"Converts text to lowercase.
CODE: LOWER('ABC') # → "abc"Makes the first letter uppercase and the rest lowercase.
NAME: CAPITALIZE('john doe') # → "John doe"Capitalizes the first letter of each word.
NAME: TITLE('john doe') # → "John Doe"Removes whitespace from both ends of the string.
CLEAN: TRIM(' hello ') # → "hello"Removes leading whitespace.
CLEAN: TRIM_LEFT(' hello ') # → "hello "Removes trailing whitespace.
CLEAN: TRIM_RIGHT(' hello ') # → " hello"Returns the string length.
LENGTH: LEN('hello') # → 5Replaces all occurrences of substring old with new.
FIXED: REPLACE('foo bar foo', 'foo', 'baz') # → "baz bar baz"Returns a substring starting at start (0-based). If length is provided, limits output length.
PART: SUBSTR('hello world', 6) # → "world"
PART: SUBSTR('hello world', 0, 5) # → "hello"Returns the first n characters.
PREFIX: LEFT('abcdef', 3) # → "abc"Returns the last n characters.
SUFFIX: RIGHT('abcdef', 3) # → "def"Pads string on the left to width using char (space by default).
NUM: PAD_LEFT('42', 6, '0') # → "000042"Pads string on the right to width.
COL: PAD_RIGHT('Name', 20, '.') # → "Name................"Repeats a string n times.
LINE: REPEAT('-', 40) # → "----------------------------------------"Concatenates all arguments into one string.
FULL: CONCAT('John', ' ', 'Doe') # → "John Doe"Joins arguments using separator.
CSV: JOIN(', ', 'apple', 'banana', 'cherry') # → "apple, banana, cherry"Returns True if text contains substring sub.
HAS_AT: CONTAINS('user@mail.com', '@') # → TrueReturns True if text starts with prefix.
IS_HTTP: STARTS_WITH('https://example.com', 'https') # → TrueReturns True if text ends with suffix.
IS_PDF: ENDS_WITH('report.pdf', '.pdf') # → TrueSplits text by separator and returns item at index (0-based).
DOMAIN: SPLIT('user@example.com', '@', 1) # → "example.com"Reverses the string.
REV: REVERSE('abc') # → "cba"Counts non-overlapping substring occurrences.
SPACES: COUNT_SUBSTR('a b c d', ' ') # → 3Rounds n to decimals digits after the decimal point (default: 0).
PRICE: ROUND(19.956, 2) # → 19.96
WHOLE: ROUND(3.7) # → 4.0Rounds down to nearest integer.
LOW: FLOOR(3.9) # → 3Rounds up to nearest integer.
HIGH: CEIL(3.1) # → 4Returns absolute value.
DIFF: ABS(-15) # → 15Returns the smallest argument.
LOWEST: MIN(10, 3, 7, 1) # → 1Returns the largest argument.
HIGHEST: MAX(10, 3, 7, 1) # → 10Returns sum of all arguments.
TOTAL: SUM(100, 200, 50) # → 350Returns arithmetic mean.
AVERAGE: AVG(10, 20, 30) # → 20.0Raises base to power exp.
SQUARED: POW(2, 10) # → 1024.0Square root.
ROOT: SQRT(144) # → 12.0Converts to integer (drops fractional part).
WHOLE: INT(9.87) # → 9Converts to float.
DECIMAL: FLOAT(42) # → 42.0Formats number with thousands separators and decimal precision (default: 2).
SALARY: FORMAT_NUM(1234567.891, 2) # → "1,234,567.89"
CLEAN: FORMAT_NUM(5000, 0) # → "5,000"Returns a random integer in range [low, high].
LUCK: RANDOM_INT(1, 100) # → 42 (random)Returns current date. Default format: dd.mm.yyyy.
NOW: TODAY() # → "16.02.2026"Builds a date from components.
DEADLINE: DATE(2026, 12, 31) # → "31.12.2026"With one argument, returns numeric component of current date.
With multiple arguments, returns date in dd.mm.yyyy format (component order follows args).
| Component | Result (February 16, 2026) |
|---|---|
year |
2026 |
month |
2 |
day |
16 |
YEAR: CURRENT_DATE_NUM(year) # → 2026
FULL_DATE: CURRENT_DATE_NUM(day, month, year) # → "16.02.2026"Returns string representation of date component.
| Component | Result (February 16, 2026) |
|---|---|
month |
"February" |
day |
"Monday" |
year |
"2026" |
| (none) | "February 16, 2026" |
MONTH: CURRENT_DATE_STR(month) # → "February"
DAY: CURRENT_DATE_STR(day) # → "Monday"
FULL: CURRENT_DATE_STR() # → "February 16, 2026"Formats date with strftime pattern.
| Code | Description | Example |
|---|---|---|
%Y |
Year (4 digits) | 2026 |
%m |
Month (01–12) | 02 |
%d |
Day (01–31) | 16 |
%B |
Month name | February |
%A |
Weekday name | Monday |
%H |
Hours (00–23) | 14 |
%M |
Minutes (00–59) | 30 |
ISO_DATE: DATE_FORMAT(TODAY(), '%Y-%m-%d') # → "2026-02-16"
PRETTY: DATE_FORMAT(DATE(2026, 3, 8), '%d %B %Y') # → "08 March 2026"Returns ISO weekday number (Monday = 1 ... Sunday = 7). Without arguments, uses today.
DOW: DAY_OF_WEEK() # → 1 (Monday)
DOW: DAY_OF_WEEK(DATE(2026, 1, 1)) # → 4 (Thursday)Returns number of days between two dates (date_b - date_a).
Negative if date_a is later than date_b.
DIFF: DAYS_BETWEEN(DATE(2026, 1, 1), DATE(2026, 2, 1)) # → 31Creates day interval for date arithmetic.
PERIOD: "{CURRENT_DATE_NUM(day, month, year) - DAYS(7)} — {CURRENT_DATE_NUM(day, month, year)}"
# → "09.02.2026 — 16.02.2026"Creates week interval.
NEXT: "{TODAY() + WEEKS(2)}" # → date in 2 weeksApproximate month interval (30 days = 1 month).
LATER: "{TODAY() + MONTHS(3)}" # → date in ~3 monthsApproximate year interval (365 days = 1 year).
EXPIRY: "{TODAY() + YEARS(1)}" # → date in ~1 yearIf condition is true, returns then_value; otherwise returns else_value (or empty string).
STATUS: IF(PRICE > 1000, 'Premium', 'Standard')
DISCOUNT: IF(PRICE >= 500, PRICE * 0.1, 0)Returns first argument that is not None.
NAME: COALESCE(SQL('SELECT name FROM clients WHERE id = 1'), 'Unknown')Returns value if it is not None, otherwise fallback.
CITY: DEFAULT(SQL('SELECT city FROM clients WHERE id = 1'), 'N/A')Returns True if value is not None.
HAS_NAME: DEFINED(SQL('SELECT name FROM clients WHERE id = 1'))Logical negation.
IS_EMPTY: NOT(DEFINED(SQL('SELECT name FROM clients WHERE id = 1')))Returns True if all arguments are truthy.
VALID: AND(PRICE > 0, LEN(TITLE) > 0)Returns True if at least one argument is truthy.
ALERT: OR(PRICE > 10000, DAYS_LEFT < 3)Returns value by index (0-based).
# Quarter by month
QUARTER: CHOOSE(CURRENT_DATE_NUM(month) % 4, 'Q1', 'Q2', 'Q3', 'Q4')Matches value against case → result pairs. If no case matches, returns default
(the last unpaired argument) or None.
LABEL: SWITCH(STATUS, 'draft', 'Draft', 'sent', 'Sent', 'Unknown')Returns environment variable value. If variable is missing, returns fallback (default: "").
USER: ENV('USER') # → "flacsy"
HOME: ENV('HOME', '/tmp') # → "/home/flacsy"Executes SQL query against SQLite database.
- SELECT — returns first column of first row (or
Noneif result is empty). If query has multiple columns, returns a tuple. - INSERT / UPDATE / DELETE / CREATE — executes query as side effect, returns
None.
ON_START:
- SQL('CREATE TABLE IF NOT EXISTS doc (num INTEGER DEFAULT 0)')
- SQL('INSERT OR IGNORE INTO doc (rowid, num) VALUES (1, 0)')
INVOICE:
SQL('SELECT num FROM doc WHERE rowid = 1') + 1
ON_END:
SQL('UPDATE doc SET num = num + 1 WHERE rowid = 1')These keys are not placeholders and are processed separately:
| Key | Description |
|---|---|
ON_START |
List of expressions executed before placeholder processing |
ON_END |
List of expressions executed after processing |
OUTPUT_NAME |
Output filename template (supports {PLACEHOLDER}) |
OUTPUT_FORMAT |
Export formats list (docx, pdf, ...) |
ON_START:
- SQL('CREATE TABLE IF NOT EXISTS document (last_num INTEGER DEFAULT 0)')
- SQL('INSERT OR IGNORE INTO document (rowid, last_num) VALUES (1, 0)')
INVOICE_NUM:
SQL('SELECT last_num FROM document WHERE rowid = 1') + 1
CLIENT_NAME:
"Acme Corp"
DATE_STR:
CURRENT_DATE_STR(month)
TOTAL:
500
TAX:
ROUND(TOTAL * 0.2, 2)
GRAND_TOTAL:
TOTAL + TAX
STATUS_LABEL:
IF(GRAND_TOTAL > 1000, 'Premium Invoice', 'Standard Invoice')
FORMATTED_TOTAL:
FORMAT_NUM(GRAND_TOTAL, 2)
PERIOD:
"{CURRENT_DATE_NUM(day, month, year) - DAYS(30)} — {CURRENT_DATE_NUM(day, month, year)}"
OUTPUT_NAME:
"Invoice-{INVOICE_NUM}"
OUTPUT_FORMAT:
- docx
- pdf
ON_END:
SQL('UPDATE document SET last_num = last_num + 1 WHERE rowid = 1')