Getting Started#
The ISON language is a functional programming language that lives within the JSON or JSON5 syntax specification. That is, every ISON script file can be read as a standard JSON file. The ISON syntax only appears in strings within the JSON file.
In the following, the basic language features of ISON are presented. You can copy paste the code and run it in an Jupyter Notebook or directly in a Python file.
Note
If you have installed the module functional-json
, you can run the ISON parser also from the command line with ison
. For command line help, run ison -h
.
Special ISON Statements#
All dictionary keys in a JSON file that start with double underscore “__
” are treated as special language features.
If they are not ISON language elements, they are either removed or are reported as error.
Key |
Value Type |
Description |
---|---|---|
|
list of strings |
List of relative/absolute file paths of files to include. Go there… |
|
dictionary |
Indicates that the parent dictionary is a lambda function. See Go there… |
|
dictionary |
Global variabel definition |
|
dictionary |
Local variable definition |
|
dictionary |
Global function variable definition |
|
dictionary |
Local function variable definition |
|
dictionary |
Runtime variable definition |
|
dictionary |
Modify dictionary depending on current hardware platform. Go there… |
|
dictionary |
Only allowed inside |
Values#
As a first example, we will simply replace a value with another one.
import ison
# First we define a dictionary of macro values that can be replaced.
# When using the CML for Catharsys configuration files, this part is
# done by Catharsys.
dicVars = {"hello": "world"}
# Now we define the dictionary that is processed.
# This is your configuration json structure.
dicData = {"value": "$hello"}
dicResult = ison.run.Run(xData=dicData, dicConstVars=dicVars)
print(ison.run.ToString(dicResult))
{
"value": "world"
}
Variables can be accessed in two ways:
Using the full syntax:
${variable}
Using the variable syntax:
$variable
The full syntax will later be used to apply functions, as in $sum{1, 2}
.
If a variable is not found, this is not an error, but the element is just left as it is. This is done to enable parsing in mutliple stages, as more data becomes available.
dicData = {"value": "$hello"}
dicResult = ison.run.Run(xData=dicData)
print(ison.run.ToString(dicResult))
{
"value": "$hello"
}
Variables#
You can define variables within a dictionary, which can be accessed by other parts of the dictionary. These variables can either be declared as local or global variables.
dicData = {
"__globals__": {
"hello": "world",
},
"mValues": {
"__locals__": {
"hello": "today",
},
"value": "$hello",
},
"value": "$hello",
}
# Set 'bStripVars' to False, to see the variables dictionaries in the output.
dicResult = ison.run.Run(xData=dicData, bStripVars=True)
print(ison.run.ToString(dicResult))
{
"mValues": {
"value": "today"
},
"value": "world"
}
Local and global variables are evaluated first, before the remainder of the dictionary is processed. If you want to define variables that are executed every time they are referenced, you need to place the variables in __func_globals__
or __func_locals__
. For example,
dicData = {
"__func_globals__": {
# This variables will calculate a uniformly distributed random variable
"fRand": "$rand.uniform{0, 1}",
},
"__globals__": {
"fA": "$fRand",
"fA2": "$fA",
"fB": "$fRand",
},
"fA2": "$fA2",
"fA": "$fA",
"fB": "$fB",
"fC": "$fRand",
}
dicResult = ison.run.Run(xData=dicData, bStripVars=False)
print(ison.run.ToString(dicResult))
{
"__func_globals__": {
"fRand": "$rand.uniform{0, 1}"
},
"__globals__": {
"fA": 0.9907603148150335,
"fA2": 0.9907603148150335,
"fB": 0.7556173456393716
},
"fA2": 0.9907603148150335,
"fA": 0.9907603148150335,
"fB": 0.7556173456393716,
"fC": 0.038020755108450044
}
Here, the variables fA
and fB
are evaluated first, before the elements outside the variable dictionaries. fC
executes the function referenced by fRand
again, since fRand
is not pre-evaluated.
Lists and Dictionaries#
Lists and dictionaries are defined in the standard JSON way, with []
and {}
, respectively. Elements of lists and dictionaries are both accessed using the colon operator :
. Here is an exampe,
import ison
dicData = {
"__globals__": {
# Define a list of values
"lA": [1, 2, 3],
# Define a dictionary of values
"mB": {
"a": 1,
"b": 2,
},
},
# Pick the second value from the list
"fA": "${lA:1}",
# Pick element 'b' from the dictionary
"fB": "${mB:b}",
}
dicResult = ison.run.Run(xData=dicData, bStripVars=False)
print(ison.run.ToString(dicResult))
{
"__globals__": {
"lA": [
1,
2,
3
],
"mB": {
"a": 1,
"b": 2
}
},
"fA": 2,
"fB": 2,
"__func_globals__": {}
}
You can also use the colon operator for nested objects, as shown in the next example.
import ison
dicData = {
"__globals__": {
# Define a dictionary of structured values
"mB": {
"lU": [1, 2, 3],
"mX": {
"lA": [1, 2, 3],
"lB": [4, 5, 6],
},
},
},
# Pick a nested value
"fA": "${mB:mX:lA:1}",
}
dicResult = ison.run.Run(xData=dicData, bStripVars=False)
print(ison.run.ToString(dicResult))
{
"__globals__": {
"mB": {
"lU": [
1,
2,
3
],
"mX": {
"lA": [
1,
2,
3
],
"lB": [
4,
5,
6
]
}
}
},
"fA": 2,
"__func_globals__": {}
}
Functions#
ISON defines a number of functions that you can call with the syntax
"$func{arg1, arg2, [...]}"
where func
is the function name and arg1
, arg2
, etc. are the function arguments. Just as with variables, the function expression will be replaced by the result of the function. If the function call is the only element in the JSON string, then the whole string is replaced by the function result. For example, if the function results in a list, then the string with the function call is replaced by the list.
Here is an example:
import ison
dicData = {
"result": "$range{3}",
}
dicResult = ison.run.Run(xData=dicData, bStripVars=True)
print(ison.run.ToString(dicResult))
{
"result": [
0,
1,
2
],
"__globals__": {},
"__func_globals__": {}
}
Functions can also be nested, as in this example,
import ison
dicData = {
"result": "$sort{$range{3}, true}",
}
dicResult = ison.run.Run(xData=dicData, bStripVars=True)
print(ison.run.ToString(dicResult))
{
"result": [
2,
1,
0
]
}
Unrolling Arguments#
Lists can also be “unrolled” as function arguments, so that each list element becomes one function argument. This can be done by prepending the argument that is to be unrolled with a “*
”, as in this example.
import ison
dicData = {
# This prints a list of numbers
"result 1": "$print{$range{3}}",
# This prints three lines each with one element of the list
"result 2": "$print{*$range{3}}",
# This calculates the sum of all list elements
"sum": "$print{Sum of $range{3}: $sum{*$range{3}}}",
}
dicResult = ison.run.Run(xData=dicData, bStripVars=True)
# print(ison.run.ToString(dicResult))
[0, 1, 2]
0
1
2
Sum of [0, 1, 2]: 3
Literal arguments#
Sometimes it is helpful to pass an argument to a function without processing the argument before the function sees it. This is done by using a “^
” as first element of an argument.
import ison
dicData = {
# Make the whole argument to print a literal argument which is not parsed
# before it is passed to the function.
"literal": "$print{^Sum of $range{3}: $sum{*$range{3}}}",
# Split it into two arguments (and use the default separator ", "). The
# first argument is a literal string, the second is a parsed value.
"sum": "$print{^Sum of $range{3}:, $sum{*$range{3}}}",
}
dicResult = ison.run.Run(xData=dicData, bStripVars=True)
# print(ison.run.ToString(dicResult))
Sum of $range{3}: $sum{*$range{3}}
Sum of $range{3}:
3
Tuples#
Some functions, like the !foreach
function, can process tuple arguments (see documentation). These can be defined using “()
”. Note that this is only possible as an argument of a function. Tuples cannot be defined as separate variables. The function $group{}
returns a list of tuples, that can be unrolled and used in the !foreach
function, to call a lambda function repeatedly with a set of values. Tuples are converted to lists when they are the final result of a function and need to be embedded in JSON.
import ison
dicData = {
"result": "$print{$group{$range{3}, $range{3,5}}}"
}
dicResult = ison.run.Run(xData=dicData, bStripVars=True)
# print(ison.run.ToString(dicResult))
[(0, 3), (1, 4), (2, 5)]
Strings#
All ISON commands are contained in a string within a JSON file. Therefore, all arguments you pass to an ISON function are strings, which may be transformed to a numerical value, if the function expects a number. However, sometimes the formatting of a string conflicts with the syntax of an ISON function. For example, if you want to print a text with the $print{}
function that contains a comma, then ISON would interpret this as two parameters passed to the function. In this case, you need to enclose the string you want to print either in single quotes '
or single backward quotes.
The single quotes ensure that everything between them is regarded as a single string. However, the quotes themselves are also part of that string. With backquotes, however, the quotes are stripped from the string before it is passed into the function.
Here is an example.
import ison
dicData = {
# The single parameter of the function is just a string
"_1": "$print{1. Hello World}",
# The comma here, is interpreted as a separator between parameters.
# If multiple parameters are given, each is printed on a new line.
"_2": "$print{2. Hello, World!}",
# Enclosing the string in single quotes, will prevent the comma from
# being interpreted as a separator. However, the single quotes will
# also be printed.
"_3": "$print{'3. Hello, World!'}",
# Enclosing the string in backticks, will prevent the comma from
# being interpreted as a separator. The backticks will not be printed.
# In fact, after interpreting everything between the backticks as a single
# parameter, the backticks are stripped from the parameter.
# The function then only receives the string 'Hello, World!' as a parameter, without quotes.
"_4": "$print{`4. Hello, World!`}",
}
dicResult = ison.run.Run(xData=dicData, bStripVars=False)
# print(ison.run.ToString(dicResult))
1. Hello World
2. Hello
World!
'3. Hello, World!'
4. Hello, World!