Skip to content

Commit

Permalink
Add basic assertion article, extend ternary operator description, fixes
Browse files Browse the repository at this point in the history
  • Loading branch information
lahmatiy committed Sep 21, 2023
1 parent d44456d commit 42460ef
Show file tree
Hide file tree
Showing 9 changed files with 129 additions and 27 deletions.
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
- Reduced parser size by 15Kb
- Changed `setup()` function to take `options` parameter instead of custom methods dictionary, i.e. `setup(methods)``setup({ methods })`
- Added `assertions` option for `jora()` and `setup()` functions to specify additional assertion functions, i.e. `jora(..., { assetions })` and `setup({ assertions })`
- Forbidden to override built-in methods and assertions, now `setup()` and `query()` functions throws when a custom method or assertion has the same name as built-in one
- Forbidden to override built-in methods and assertions, now `setup()` and `query()` functions throws when a custom method or an assertion has the same name as built-in one
- Extended query result object in stat mode to provide a result value of the query execution as `value` property (i.e. `jora(query, { stat: true })().value`)
- Renamed `SortingFunction` AST node type into `CompareFunction`
- Renamed `Unary` AST node type into `Prefix`
Expand Down
1 change: 1 addition & 0 deletions docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
- [Syntax overview](./articles/syntax-overview.md)
- [Literals](./articles/literals.md)
- [Operators](./articles/operators.md)
- [Assertions](./articles/assertions.md)
- [Variables](./articles/variables.md)
- [Object literals](./articles/object-literal.md)
- [Array literals](./articles/array-literal.md)
Expand Down
63 changes: 63 additions & 0 deletions docs/articles/assertions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
# Assertions

TBD

## Syntax

```jora
expr is assertion
```

> Note: `expr` can be omitted, i.e. `is assertion` is also valid form
An assertion can be an expression using `not`, `and`, `or` and grouping operators:

```
is not assertion
is (assertion and assertion)
is (assertion or not assertion)
is not (assertion and assertion)
is (assertion and (assertion or assertion))
```

## Built-in assertions

Jora comes with a set of built-in assertions which perform most common examinations on values.

Assertion | Descripton
----------|-----------
`string` | `typeof e === 'string'`
`number` | `typeof e === 'number'`
`int` | `Number.isInteger(e)`
`finite` |
`NaN` |
`Infinity` | `e === Infinity \|\| e === -Infinity`
`boolean` | `e === true \|\| e === false`
`null` | `e === null`
`undefined` | `e === undefined`
`nullish` | `e === null \|\| e === undefined`
`object` | `e !== null && typeof e === 'object' && e.constructor === Object`
`array` | `Array.isArray(e)`
`regexp` | `e.toString() === '[object RegExp]'`
`truthy` | [bool()](./methods-builtin.md#bool) method returns `true`
`falsy` | [bool()](./methods-builtin.md#bool) method returns `false`

## Custom assertions

To define custom assertions in Jora use the following API:

```js
import jora from 'jora';

// Setup query factory with custom methods
const createQueryWithCustomAssertions = jora.setup({
assertions: {
mine($) {
/* test a value */
}
}
});

// Create a query
const queryWithMyAssertion = createQueryWithCustomAssertions('is mine');
```
8 changes: 5 additions & 3 deletions docs/articles/methods.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,15 +24,17 @@ See [Built-in methods](./methods-builtin.md).

## Custom methods

To provide custom methods in Jora use the following API:
To define custom methods in Jora use the following API:

```js
import jora from 'jora';

// Setup query factory with custom methods
const createQueryWithCustomMethods = jora.setup({
myMethod($) {
/* do something and return a new value */
methods: {
myMethod($) {
/* do something and return a new value */
}
}
});

Expand Down
73 changes: 54 additions & 19 deletions docs/articles/operators.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ Jora offers a variety of operators to perform operations, comparisons, and boole
- [Arithmetic operators](#arithmetic-operators)
- [Comparison operators](#comparison-operators)
- [Logical operators](#logical-operators)
- [Ternary operator](#ternary-operator)
- [Grouping operator](#grouping-operator)
- [Pipeline operator](#pipeline-operator)
- [Operator precedence](#operator-precedence)
Expand All @@ -13,8 +14,8 @@ Jora offers a variety of operators to perform operations, comparisons, and boole

| Jora | Description |
|------|-------------|
| `x + y` | Add. In case one of the operands is an array, it produces a new array with elements from `x` and `y`, excluding duplicates.
| `x - y` | Subtract. In case left operand is an array, it produces a new array with elements from `x`, excluding elements from `y`.
| `x + y` | Add. In case one of the operands is an array, it produces a new array with elements from `x` and `y`, excluding duplicates
| `x - y` | Subtract. In case left operand is an array, it produces a new array with elements from `x`, excluding elements from `y`
| `x * y` | Multiply
| `x / y` | Divide
| `x % y` | Modulo
Expand Down Expand Up @@ -49,13 +50,13 @@ Jora offers a variety of operators to perform operations, comparisons, and boole

| Jora | Description |
|------|-------------|
| `x = y` | Equals (as `Object.is(x, y)` in JavaScript).
| `x != y` | Not equals (as `!Object.is(x, y)` in JavaScript).
| `x < y` | Less than.
| `x <= y` | Less than or equal to.
| `x > y` | Greater than.
| `x >= y` | Greater than or equal to.
| `x ~= y` | Match operator. Behavior depends on `y` type:<br>- RegExp – test against regexp<br>- function – test like `filter()`<br>- `null` or `undefined` – always truthy<br>- anything else – always falsy.
| `x = y` | Equals (as `Object.is(x, y)` in JavaScript)
| `x != y` | Not equals (as `!Object.is(x, y)` in JavaScript)
| `x < y` | Less than
| `x <= y` | Less than or equal to
| `x > y` | Greater than
| `x >= y` | Greater than or equal to
| `x ~= y` | Match operator. Behavior depends on `y` type:<br>- RegExp – test against regexp<br>- function – test like `filter()`<br>- `null` or `undefined` – always truthy<br>- anything else – always falsy

```jora
// Equals
Expand Down Expand Up @@ -93,13 +94,14 @@ Jora offers a variety of operators to perform operations, comparisons, and boole

| Jora | Description |
|------|-------------|
| `x or y` | Logical OR. Equivalent to `\|\|` in JavaScript, but `x` is testing for truthy with the `bool()` method.
| `x and y` | Logical AND. Equivalent to `&&` in JavaScript, but `x` is testing for truthy with the `bool()` method.
| `not x`<br>`no x` | Logical NOT. Equivalent to `!` in JavaScript, but `x` is testing for truthy with the `bool()` method.
| `x ? y : z` | Ternary operator. If `x` is truthy, return `y`, else return `z`. `x` is testing for truthy with the `bool()` method.
| `x ?? y` | The [nullish coalescing](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Nullish_coalescing), equivalent to `??` in JavaScript.
| `x in [a, b, c]`<br>`[a, b, c] has x` | Equivalent to `x = a or x = b or x = c`.
| `x not in [a, b, c]`<br>`[a, b, c] has no x` | Equivalent to `x != a and x != b and x != c`.
| `x or y` | Logical OR. Equivalent to `\|\|` in JavaScript, but `x` is testing for truthy with the [bool()](./methods-builtin.md#bool) method
| `x and y` | Logical AND. Equivalent to `&&` in JavaScript, but `x` is testing for truthy with the [bool()](./methods-builtin.md#bool) method
| `not x`<br>`no x` | Logical NOT. Equivalent to `!` in JavaScript, but `x` is testing for truthy with the [bool()](./methods-builtin.md#bool) method
| `x ?? y` | The [nullish coalescing](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/
Nullish_coalescing), equivalent to `??` in JavaScript
| `x is assertion` | Tests a value against an assertion, see [Assertions](./assertions.md)
| `x in [a, b, c]`<br>`[a, b, c] has x` | Equivalent to `x = a or x = b or x = c`
| `x not in [a, b, c]`<br>`[a, b, c] has no x` | Equivalent to `x != a and x != b and x != c`


```jora
Expand All @@ -119,9 +121,11 @@ no false // true
not [] // true
not [1] // false
// Ternary operator
true ? 'yes' : 'no' // 'yes'
false ? 'yes' : 'no' // 'no'
// Nullish coalescing
null ?? 1 // 1
undefined ?? 1 // 1
false ?? 1 // false
1234 ?? 1 // 1234
// IN operator
1 in [1, 2, 3] // true
Expand All @@ -140,6 +144,37 @@ false ? 'yes' : 'no' // 'no'
[1, 2, 3] has no 4 // true
```

## Ternary Operator

The ternary operator requires three operands: a condition, followed by a question mark (`?`), an expression for a truthy condition, a colon (`:`), and an expression for a falsy condition. The condition is evaluated using the [bool()](./methods-builtin.md#bool) method.

```jora
true ? 'yes' : 'no' // Result: 'yes'
```
```
false ? 'yes' : 'no' // Result: 'no'
```

Operands can be omitted. When omitted, `$` is used as a default for the condition and the truthy expression, and `undefined` for the falsy expression.

```jora
?: // Equivalents to `$ ? $ : undefined`
```

A query to truncate strings in array longer than 10 characters:

```jora
['short', 'and a very long string']
.(size() < 10 ?: `${slice(0, 10)}...`)
// Result: ["short", "and a very..."]
```

If the falsy operand is omitted, the colon (`:`) can also be omitted. This is useful in conjunction with [assertions](./assertions.md) and statistical or mathematical methods:

```jora
numbers.sum(=> is number ? $ * $)
```

## Grouping operator

Parentheses `( )` serve as the grouping operator, allowing explicitly define the order in which operations should be executed, ensuring that specific calculations are performed before others. This is particularly useful when dealing with complex expressions, as it helps to avoid unexpected results due to the default operator precedence.
Expand Down
2 changes: 1 addition & 1 deletion docs/articles/variables.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Variables <!-- omit in toc -->
# Variables

Jora allows defining and using variables within queries. A value can be assigned to a variable only on its definition. Once a variable is defined, its value cannot be changed throughout the query. Variables are useful for storing intermediate results, improving readability, reusing expressions and preserving values across scopes.

Expand Down
4 changes: 3 additions & 1 deletion docs/discovery/data.js
Original file line number Diff line number Diff line change
Expand Up @@ -119,11 +119,13 @@ function processChangelog(changelog, methods) {
if (token.type === 'list') {
for (let item of token.items) {
const [typeOfChange] = item.text.match(/^\S+/);
const prelude = item.text.split(/[;.]/)[0];
const prelude = item.text.split(/;|\b\.\B/)[0].replace(/\((?:`[^`]+`|[^)])*\)\s*/g, m => m.trim().length === 2 ? '()' : '');
const methodRefs =
prelude.match(/`[a-z\d]+\([^\)]*?\)`(?=(?:\s*(?:and|,)\s*`[a-z\d]+\([^\)]*?\)`)*\s*method)/ig) ||
prelude.match(/(?<=methods?\s*(?::\s*)?(?:`[a-z\d]+\([^\)]*?\)`\s*(?:and|,)\s*)*)`[a-z\d]+\([^\)]*?\)`/ig);

if (methodRefs) {
console.log(methodRefs)

Check failure on line 128 in docs/discovery/data.js

View workflow job for this annotation

GitHub Actions / Lint

Missing semicolon
for (const methodRef of methodRefs) {
const methodName = methodRef.slice(1).match(/^[^\(]+/);

Expand Down
1 change: 0 additions & 1 deletion docs/discovery/prepare.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
const CodeMirror = require('codemirror');
require('codemirror/addon/mode/simple');
const jora = require('jora');
const { Slugger } = require('marked');
const { parseExample } = require('./parse-example.js');
const { utils: { base64 } } = require('@discoveryjs/discovery');
Expand Down
2 changes: 1 addition & 1 deletion src/assertions.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { isPlainObject, isRegExp, isTruthy } from './utils/misc.js';
export default Object.freeze({
number: value => typeof value === 'number',
int: Number.isInteger,
finite: value => isFinite(Number(value)),
finite: value => isFinite(value),
NaN: value => isNaN(value),
Infinity: value => value === Infinity || value === -Infinity,
string: value => typeof value === 'string',
Expand Down

0 comments on commit 42460ef

Please sign in to comment.