Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
119 changes: 119 additions & 0 deletions docs/syntax.md
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,13 @@ Besides the "operator" functions, there are several pre-defined functions. You c
| count(a) | Returns the number of items in an array. |
| map(f, a) | Array map: Pass each element of `a` the function `f`, and return an array of the results. |
| fold(f, y, a) | Array fold: Fold/reduce array `a` into a single value, `y` by setting `y = f(y, x, index)` for each element `x` of the array. |
| reduce(f, y, a) | Alias for `fold`. Reduces array `a` into a single value using function `f` starting with accumulator `y`. |
| filter(f, a) | Array filter: Return an array containing only the values from `a` where `f(x, index)` is `true`. |
| find(f, a) | Returns the first element in array `a` where `f(x, index)` is `true`, or `undefined` if not found. |
| some(f, a) | Returns `true` if at least one element in array `a` satisfies `f(x, index)`, `false` otherwise. |
| every(f, a) | Returns `true` if all elements in array `a` satisfy `f(x, index)`. Returns `true` for empty arrays. |
| unique(a) | Returns a new array with duplicate values removed from array `a`. |
| distinct(a) | Alias for `unique`. Returns a new array with duplicate values removed. |
| indexOf(x, a) | Return the first index of string or array `a` matching the value `x`, or `-1` if not found. |
| join(sep, a) | Concatenate the elements of `a`, separated by `sep`. |
| naturalSort(arr) | Sorts an array of strings using natural sort order (alphanumeric-aware). For example, `["file10", "file2", "file1"]` becomes `["file1", "file2", "file10"]`. |
Expand All @@ -146,6 +152,19 @@ Besides the "operator" functions, there are several pre-defined functions. You c
| if(c, a, b) | Function form of c ? a : b. Note: This always evaluates both `a` and `b`, regardless of whether `c` is `true` or not. Use `c ? a : b` instead if there are side effects, or if evaluating the branches could be expensive. |
| coalesce(a, b, ...) | Returns the first non-null and non-empty string value from the arguments. Numbers and booleans (including 0 and false) are considered valid values. |

### Type Checking Functions

| Function | Description |
|:------------- |:----------- |
| isArray(v) | Returns `true` if `v` is an array, `false` otherwise. |
| isObject(v) | Returns `true` if `v` is an object (excluding null and arrays), `false` otherwise. |
| isNumber(v) | Returns `true` if `v` is a number, `false` otherwise. |
| isString(v) | Returns `true` if `v` is a string, `false` otherwise. |
| isBoolean(v) | Returns `true` if `v` is a boolean, `false` otherwise. |
| isNull(v) | Returns `true` if `v` is null, `false` otherwise. |
| isUndefined(v)| Returns `true` if `v` is undefined, `false` otherwise. |
Copy link

Copilot AI Jan 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Markdown table row for isUndefined is missing a space before the column separator (| isUndefined(v)| ...), which makes the table formatting inconsistent with the surrounding rows. Consider aligning it with the other entries (| isUndefined(v) | ...).

Suggested change
| isUndefined(v)| Returns `true` if `v` is undefined, `false` otherwise. |
| isUndefined(v) | Returns `true` if `v` is undefined, `false` otherwise. |

Copilot uses AI. Check for mistakes.
| isFunction(v) | Returns `true` if `v` is a function, `false` otherwise. |

## String Functions

The parser includes comprehensive string manipulation capabilities.
Expand Down Expand Up @@ -403,6 +422,106 @@ map(x => x > 5 ? "high" : "low", [3, 7, 2, 9]) // Using ternary operator

> **Note:** Arrow functions share the same `fndef` operator flag as traditional function definitions. If function definitions are disabled via parser options, arrow functions will also be disabled.

### Examples of New Array Functions

The new array utility functions provide additional ways to work with arrays:

**Using reduce (alias for fold):**

```js
reduce((acc, x) => acc + x, 0, [1, 2, 3, 4]) // 10 (sum using reduce)
reduce((acc, x) => acc * x, 1, [2, 3, 4]) // 24 (product)
```

**Using find:**

```js
find(x => x > 5, [1, 3, 7, 2, 9]) // 7 (first element > 5)
find(x => x < 0, [1, 2, 3]) // undefined (not found)
find(x => x.age > 18, users) // First user over 18
```

**Using some and every:**

```js
some(x => x > 10, [1, 5, 15, 3]) // true (at least one > 10)
every(x => x > 0, [1, 2, 3, 4]) // true (all positive)
every(x => x % 2 == 0, [2, 4, 5, 6]) // false (not all even)
some(x => x < 0, [1, 2, 3]) // false (none negative)
```

**Using unique/distinct:**

```js
unique([1, 2, 2, 3, 3, 3, 4]) // [1, 2, 3, 4]
distinct(["a", "b", "a", "c", "b"]) // ["a", "b", "c"]
unique([]) // []
```

**Combining array functions:**

```js
// Filter positive numbers, remove duplicates, then double each
unique(filter(x => x > 0, [1, -2, 3, 3, -4, 5, 1])) // [1, 3, 5]
map(x => x * 2, unique([1, 2, 2, 3])) // [2, 4, 6]

// Find first even number greater than 5
find(x => x % 2 == 0, filter(x => x > 5, [3, 7, 8, 9, 10])) // 8
```

### Examples of Type Checking Functions

Type checking functions are useful for validating data types and conditional logic:

**Basic type checking:**

```js
isArray([1, 2, 3]) // true
isNumber(42) // true
isString("hello") // true
isBoolean(true) // true
isNull(null) // true
isUndefined(undefined) // true
isObject({a: 1}) // true
isFunction(abs) // true
```

**Using with conditionals:**

```js
if(isArray(x), count(x), 0) // Get array length or 0
if(isNumber(x), x * 2, x) // Double if number
if(isString(x), toUpper(x), x) // Uppercase if string
```

**Using with filter:**

```js
filter(isNumber, [1, "a", 2, "b", 3]) // [1, 2, 3]
filter(isString, [1, "a", 2, "b", 3]) // ["a", "b"]
```

**Using with some/every:**

```js
some(isString, [1, 2, "hello", 3]) // true (has at least one string)
every(isNumber, [1, 2, 3, 4]) // true (all are numbers)
every(isNumber, [1, "a", 3]) // false (not all numbers)
```

**Practical examples:**

```js
// Count how many strings are in an array
count(filter(isString, [1, "a", 2, "b", 3])) // 2

// Get the first number in a mixed array
find(isNumber, ["a", "b", 3, "c", 5]) // 3

// Check if any value is null or undefined
some(x => isNull(x) or isUndefined(x), data) // true/false
```

## Custom JavaScript Functions

If you need additional functions that aren't supported out of the box, you can easily add them in your own code. Instances of the `Parser` class have a property called `functions` that's simply an object with all the functions that are in scope. You can add, replace, or delete any of the properties to customize what's available in the expressions. For example:
Expand Down
66 changes: 66 additions & 0 deletions src/functions/array/operations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -95,3 +95,69 @@ export function count(array: any[] | undefined): number | undefined {
}
return array.length;
}

export function reduce(f: Function, init: any, a: any[] | undefined): any {
// reduce is an alias for fold
return fold(f, init, a);
}

export function find(f: Function, a: any[] | undefined): any {
if (a === undefined) {
return undefined;
}
if (typeof f !== 'function') {
throw new Error('First argument to find is not a function');
}
if (!Array.isArray(a)) {
throw new Error('Second argument to find is not an array');
}
return a.find(function (x: any, i: number): any {
return f(x, i);
});
}

export function some(f: Function, a: any[] | undefined): boolean | undefined {
if (a === undefined) {
return undefined;
}
if (typeof f !== 'function') {
throw new Error('First argument to some is not a function');
}
if (!Array.isArray(a)) {
throw new Error('Second argument to some is not an array');
}
return a.some(function (x: any, i: number): any {
return f(x, i);
});
}

export function every(f: Function, a: any[] | undefined): boolean | undefined {
if (a === undefined) {
return undefined;
}
if (typeof f !== 'function') {
throw new Error('First argument to every is not a function');
}
if (!Array.isArray(a)) {
throw new Error('Second argument to every is not an array');
}
return a.every(function (x: any, i: number): any {
return f(x, i);
});
}

export function unique(a: any[] | undefined): any[] | undefined {
if (a === undefined) {
return undefined;
}
if (!Array.isArray(a)) {
throw new Error('Argument to unique is not an array');
}
// Use Set to remove duplicates, then convert back to array
return Array.from(new Set(a));
}

export function distinct(a: any[] | undefined): any[] | undefined {
// distinct is an alias for unique
return unique(a);
}
1 change: 1 addition & 0 deletions src/functions/utility/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,4 @@

export * from './conditional.js';
export * from './string-object.js';
export * from './type-checking.js';
77 changes: 77 additions & 0 deletions src/functions/utility/type-checking.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
/**
* Type checking utility functions
* Provides functions to check the type of values
*/

/**
* Checks if a value is an array
* @param value - The value to check
* @returns True if the value is an array, false otherwise
*/
export function isArray(value: any): boolean {
return Array.isArray(value);
}

/**
* Checks if a value is an object (and not null or array)
* @param value - The value to check
* @returns True if the value is an object (excluding null and arrays), false otherwise
*/
export function isObject(value: any): boolean {
return value !== null && typeof value === 'object' && !Array.isArray(value);
}

/**
* Checks if a value is a number
* @param value - The value to check
* @returns True if the value is a number, false otherwise
*/
export function isNumber(value: any): boolean {
return typeof value === 'number';
}

/**
* Checks if a value is a string
* @param value - The value to check
* @returns True if the value is a string, false otherwise
*/
export function isString(value: any): boolean {
return typeof value === 'string';
}

/**
* Checks if a value is a boolean
* @param value - The value to check
* @returns True if the value is a boolean, false otherwise
*/
export function isBoolean(value: any): boolean {
return typeof value === 'boolean';
}

/**
* Checks if a value is null
* @param value - The value to check
* @returns True if the value is null, false otherwise
*/
export function isNull(value: any): boolean {
return value === null;
}

/**
* Checks if a value is undefined
* @param value - The value to check
* @returns True if the value is undefined, false otherwise
*/
export function isUndefined(value: any): boolean {
return value === undefined;
}

/**
* Checks if a value is a function
* @param value - The value to check
* @returns True if the value is a function, false otherwise
*/
export function isFunctionValue(value: any): boolean {
return typeof value === 'function';
}

Loading
Loading