Home

Awesome

jjay

jjay (pronounced jay-jay) is a toy JSON templating language.

Features

Syntax

A jjay script consists of zero or more statements, followed by a single expression. Every statement ends with ;, while the final expression does not.

Expressions

Expressions are pieces of jjay code that evaluate to a value.

Expressions can be any JSON-like value, such as objects, arrays, strings, numbers, booleans and null. They may also be variables (x) or function calls (f(expr)), or they may be blocks containing more statements and expressions ((...)).

Objects

Objects are written similarly to pure JSON syntax, but object keys may be unquoted if they are valid identifiers. Trailing commas are also allowed.

{
  x: 3,
  "y": 4,
}

Arrays

Arrays are written similarly to pure JSON syntax, but trailing commas are allowed:

[ 1, 2, 3, ]

Other JSON value types

Strings, numbers, booleans and null are identical to their pure JSON equivalents.

Variable declarations

Variables are declared with the let statement:

let x = 3;
let y = 4;
[ x, y ] // = [ 3, 4 ]

Variables may not have the same name as a previously declared variable, except:

Function declarations

Functions are declared with the let statement, with one or more argument group:

let f(x) = x + 1;
let g(x)(y) = x + x * y;
f(1) + g(2)(3) // = 10

A function inherits the scope outside it, but variables within it may shadow variables in the outer scope.

let x = 3;
let f(x) = x + 3;
f(5) // = 8

Lambda function

A lambda function is declared with the syntax:

(x => expr)

where x is the argument and expr is the expression body of the lambda function. Note that the surrounding parentheses are mandatory.

For simplicity, chained lambda functions only require the outermost set of parentheses:

(x => y => z => x + y + z)

Binary operators

Most operators are implemented as built-in functions.

jjay has the following arithmetic operators:

jjay has the following comparison operators:

Note: The comparison operators are understood by jjay, but their associated functions have not been implemented.

Numbers are compared numerically. Strings and arrays are compared lexiographically. true is greater than false. Values of different types may not be compared, except for equality or inequality.

jjay also has a "pipeline" operator, |. The right-hand side is invoked as a function with the left-hand side as the argument, so x | f is equivalent to f(x).

OperatorFunction namePrecedenceAssociativity
|/pipe0Left-to-right
==/eq1Left-to-right
!=/ne1Left-to-right
<=/le1Left-to-right
>=/ge1Left-to-right
</lt1Left-to-right
>/gt1Left-to-right
+/add2Left-to-right
-/sub2Left-to-right
*/mul3Left-to-right
//div3Left-to-right

Paths

Values in objects can be access as object.key or object["key"], where "key" may be any valid expression that can be converted to a string.

Values in arrays can be accessed as array[index], where index may be any valid expression that can be converted to an 32-bit unsigned integer. (Attempting to use numbers outside of this range may result in undefined behavior.)

Null propagation

In paths or function calls, the null propagation operator ? may be used to propagate null values or deal with empty properties:

null?.x.y.z // = null
null?() // = null

Blocks

A block is zero or more statements, followed by a single expression. While similar to a script, a block must always be surrounded by parentheses.

A block inherits the scope outside it, but variables within it may shadow variables in the outer scope.

Built-in variables and functions