Inferno is a simple language used for reading and/or writing to control/virtual parameters within OnPing. Below, we give an overview of the core features of this language and some examples of sample scripts the users can write.

## Types #

A core feature of the Inferno language is its type system. Types in a programming language are a way of labeling all values with the kind of “thing” they represent. For instance, within the Inferno language, there are values which are numeric, textual or represent time/timezones (amonst many others). Types can provide useful information to the user and help prevent many common errors when a script contains a mistake (like adding a boolean to an integer, or passing the wrong parameter to a function). These are some of the types present in the standard library:

`int`

: Integer numbers. These are numbers without decimals.`double`

: Double-precision floating numbers. These are numbers with decimals.`bool`

: Truth values. This type has only two members:`#true`

and`#false`

.`epochTime`

: Points in time.`word16`

: Unsigned 16-bit integeres.`word32`

: Unsigned 32-bit integeres.`word64`

: Unsigned 64-bit integeres.

Besides these “basic” types, there are several more complex types which can be built up from the types above:

`array of *`

: Where`*`

can be`int`

,`double`

or potentially another array or other complex type`series of *`

: This type represents the values of a control/virtual parameter. Given a`series of`

double, using functions such as`latest`

, we can extract the latest`double`

value in the series.`option of *`

: A type such as`option of int`

represents a value which could potentially be undefined. This is useful to indicate a value is missing for a certain time in a time series, for example.`* -> *`

(function type): A value can also be a function. For instance, a function

that takes a`double`

and returns an`int`

has type`double -> int`

.`(*, ..., *)`

(tuple type): We can bundle several different types into a tuple, useful if we want to return multiple value from a function, for example a function with the type`double -> (int, text)`

takes a`double`

value and returns both an`int`

and a`text`

value. The unit/empty tuple`()`

can be used to represent no (useful) value is being returned.

There is no need to tell the system what type any of the literals and variables are. It can deduce this iformation automatically and will only warn the user in case of a mistake.

## Language features #

### Literals

Literals are values that you write directly in the script, like `0.8`

or `120`

.

There are three kinds of literals:

- Numeric literal (with decimals):
`0.80`

,`100.0`

, etc. They have type`double`

. - Numeric literal (without decimals):
`120`

,`1234`

, etc. They have type`int`

. - Hexadecimal literal:
`0xA9F`

,`0x8D01`

, etc. They have type`word64`

. - Text/string literal:
`"Hello"`

They have type`text`

. - Interpolated string:
``Hello ${23} ${"world"}!``

They have type`text`

.

### Variables

Variables are names that represent values, like `foo`

or `bar`

. They always start with a letter and are followed by zero or more letters or numbers. Examples of valid variables are: `x`

, `y`

, `MyVariable`

, `ThisIsAVariable`

, `this2isavariable`

, `Year2018`

.

### Optionals

Certain functions in the standard library are not guaranteed to return a value. This is indicated by the optional type, which has two values:

- If a value is present, it is written using the
`Some`

keyword. For example the expression`Some 120`

denotes an`option of int`

, whereas`Some "Hello world!"`

has type`option of text`

. - If a value is missing, we indicate this using the
`None`

keyword.

### Tuples

Values, variables and other constructs can be grouped together into tuples, for example, if a function returns multiple values. Tuples are formed by appending values in brackets, e.g. `(0.80, "Hello world!")`

, which has the type `(int, text)`

. We can also indicate the return of a “unit” value by the unit/empty tuple `()`

, not to be confused with the optional value `None`

.

### Enums

An enum is a user defined set of names/labels which can be stored inside a control parameter instead of a simple `double`

value. Using an enum may be useful, if the parameter value represents a discrete state of a system rather than a continuous value, such as pressure or flow rate. For example, the `bool`

type is an enum with the labels `#true`

and `#false`

. Another example of an enum might be a `plungerState`

enum, with `#rising`

, `#falling`

or `#disabled`

states. Using enums has multiple advantages described further below, not least of which is better readability of scripts.

### Functions

Like we mentioned earlier (see the Types section), a variable can contain a function. To apply an argument to a function, simply put the argument you want to pass tothe function right after the function variable. For example, if we have a function called `f`

of type `double -> bool`

, we can apply an argument `3.14`

to `f`

, simply by writing `f 3.14`

. The result will have type `bool`

.

### Let assignments

A let statement can be used to introduce temporary/intermediate values within a script:

```
let x = <code 1>
in <code 2>
```

### Conditionals

Inferno includes the familiar `if...then...else...`

sconstruct. The syntax goes as

follows:

```
if <boolean expression>
then
<code 1>
else
<code 2>
```

Unlike most programming languages, `if`

statements in Inferno are expressions. This means we can assign an `if`

expression to a variable or pass it to a function:

```
let x = if 2 > 3 then 5 else 6 in
x + 1
```

### Case statements

The `match`

statement is like a more powerful `if`

statement, which allows us to pattern match not only on `bool`

eans but also other enums as well as optional types. The syntax for a `match`

is as follows:

```
match <expression> with {
| <pattern 1> ->
<code 1>
...
| <pattern n> ->
<code n>
}
```

Like the `if`

statement, a `match`

is also an expression, assignable to a variable, which means all the branches must return a value of the same type. A `match`

statement can be used instead of an `if`

statement:

```
let x = match 2 > 3 with {
| #true -> 5
| #false -> 6
}
in x + 1
```

It can also be used to inspect the value of an optional type:

```
match latest pid1 with {
| Some val -> val * 2
| None -> 0
}
```

In this instance, we analyze the result of calling `latest pid1`

, which may rerturn a `None`

value. The branches of a `match`

must always be total, namely, we must handle all the cases for any given type, i.e. we must have a `#true`

and a `#false`

branch for `bool`

, a `Some x`

and `None`

for `option of *`

, etc. If we only care about one specific case and want to lump all the other cases into a “catch all” we can use the wildcard `_`

pattern:

```
match someNumber with {
| 0 -> 1
| 1 -> 0
| _ -> 42
}
```

It is also possible to match on multiple values at once with the use of tuples:

```
match (latest pid1, someNumber) with {
| (Some v, 0) -> v
| (Some v, 1) -> (-v)
| (Some _, _) -> 42
| (None, 0) -> 1
| (None, 1) -> 0
| _ -> 84
}
```

### Assertions

Assertions in scripts can be used to check invariants/assumptions made about the data being processed, e.g.:

```
assert latestTempReading > 0
in <code>
```

If the assertion is violated during the execution of the script an error is logged

### Arrays

Arrays can be useful in scripts when pulling data from several virtual/control parameters and aggregating the results. The Inferno language does not offer direct manipulation of arrays via indices, to prevent usual errors with out of bounds array access, etc. However, the language includes array builder notation which allow for modifying and filtering arrays. The syntax of array builders is thr following:

`[ <expr> | x1 <- <array 1> , ... xn <- <array n>, (if <boolean condition>) ]`

The array builder notation allows for filtering of elements

`[ n | n <- someNumberArray, if n > 10 ]`

applying a transformation to each element of an array

`[ 2 * n | n <- someNumberArray ]`

or combining several arrays by taking their product

`[ (n, m) | n <- someNumberArray, m <- someOtherArray ]`

### Modules

Modules are a way of organizing reusable functions/scripts into units which another script can depend on and import.

## Predefined Functions and Operators #

Precedence | Left | None | Right |
---|---|---|---|

11 | ** | ||

10 | *, /, %, .&. | ||

9 | +, -, `.XOR.` | ||

8 | .|. | ||

7 | <,>, >=, <= | ||

6 | ==, != | ||

5 | && | ||

4 | XOR | ||

3 |

### Arithmetic

Addition (`+`

) and subtraction (`-`

) can be used with `Int`

, `Double`

, and `EpochTime`

.

Multiplication and division only with `Int`

and `Double`

. Division *always* gives a result

of type `Double`

, to account for potential inexact divisions.

Exponentiation is done with `**`

. For example, `2 ** 3 = 8`

.

### Boolean Values

The boolean values are `#true`

and `#false`

.

The function `not :: bool -> bool`

gives `#true`

for `#false`

, and `#false`

for `#true`

.

### Logical Operators

`|| :: bool -> bool -> bool`

: Logical disjunction.`OR`

is also accepted.`&& ::`

: Logical conjunction.`bool`

->`bool`

->`bool`

`AND`

is also accepted.`XOR ::`

: Exclusive disjunction.`bool`

->`bool`

->`bool`

`isSet :: int -> int -> bool`

: Check if the bit indicated by the second argument is set.

For any`n`

:`0 isSet n = #false`

.

### Integer Operators

`MOD :: Int -> Int -> Int`

: Modulo operation. Example:`9 MOD 4 = 1`

.

### Truncation Operator

The operator `` :: Double -> Int -> Double`

truncates a number to the given number of decimals. `TRUNC`

is also accepted.

### Rounding Functions

All rounding functions return integers. They accept both `Double`

and `Int`

arguments, but they

leave `Int`

values unmodified. The rounding functions are:

`floor`

: Round to the closest smaller (or equal) integer.`ceiling`

: Round to the closest bigger (or equal) integer.`round`

: Round to the closest integer.`truncate`

: Round by removing the decimal part.

### Comparison Operators

All operators work with `Int`

and `Double`

, and only values of the *same type* can be compared.

The available operators are:

`==`

: Equality. The use of equality with`Double`

is not recommended.`<>`

: Inequality.`<`

: Less than.`<=`

: Less or equal to.`>`

: Greater than.`>=`

: Greater or equal to.

### Comparison Functions

The functions `max`

and `min`

have two arguments can be used with values of `Int`

, `Double`

, and `EpochTime`

.

They return the smallest (or largest) of its arguments. For example: `max(-2,1) = 1`

, `min(-3,2) = -3`

.

### Sign Functions

The functions `abs`

and `negate`

can be applied to values of type `Int`

or `Double`

. The function`abs`

returns the absolute value of a number, and the function `negate`

calculates the opposite

number.

### Time Functions

`now :: EpochTime`

: A constant value with the current time. Its value does*not*change during the

execution of the script.`seconds :: Int -> EpochTime`

: A number of seconds.`minutes :: Int -> EpochTime`

: Minutes.`hours :: Int -> EpochTime`

: Hours.`weeks: Int -> EpochTime`

: Weeks.`monthsAgo :: Int -> EpochTime`

: The time a given number of months ago.`hour :: EpochTime -> EpochTime`

: Returns time at the start of the hour.`day :: EpochTime -> EpochTime`

: Returns time at the start of the day (midnight).`month :: EpochTime -> EpochTime`

: Returns time at the start of the month.`year :: EpochTime -> EpochTime`

: Returns time at the start of the year.

### Casting Functions

`double :: Int -> Double`

: Cast an`Int`

value to a`Double`

value.`boolToInt :: Bool -> Int`

: Cast a`Bool`

value to an`Int`

value.`False`

becomes`0`

, and`True`

becomes`1`

.`intToBool :: Int -> Bool`

: Cast an`Int`

value to a`Bool`

value.`0`

becomes`False`

, and anything else`True`

.`timeToInt :: EpochTime -> Int`

: Cast an`EpochTime`

value to an`Int`

value. The result is the number of

seconds since January 1, 1970, 00:00, not counting leap seconds. You can use`seconds`

to get the`EpochTime`

back.`doubleBits :: Double -> Word64`

: Cast a`Double`

value to an unsigned integer that has the same bits set.

### Floating Point Number Functions

`recip :: Double -> Double`

: Calculate the inverse of a number. For example:`recip(2) = 0.5`

.`sqrt :: Double -> Double`

: Calculate the square root of a number.`limit :: Double -> Double -> Double -> Double`

. The expression`limit(l,u,x)`

will evaluate to:`l`

if`x <= l`

.`u`

if`x >= u`

.`x`

otherwise.

### Unsigned Integer Functions

These functions work with `Word16`

, `Word32`

and `Word64`

types. We would refer to them as `WordN`

in the following

functions.

`testBit :: WordN -> Int -> Bool`

: Test if the nth bit is 1.`setBit :: WordN -> Int -> WordN`

: Set the nth bit to 1.`clearBit :: WordN -> Int -> WordN`

: Set the nth bit to 0.`complementBit :: WordN -> Int -> WordN`

: Set the nth bit to 0 if it’s 1, or to 1 otherwise.`complement :: WordN -> WordN`

: Complement every bit. Example:`complement(0xF0) = 0x0F`

.`shift :: WordN -> Int -> WordN`

: Shift the given number of bits. The number can be negative for

a negative shift. Examples:`shift(0x0F0,4) = 0xF00`

,`shift(0x0F0,-4) = 0x00F`

,`shift(0x0F0,-8) = 0x0`

.`fromWord :: WordN -> Int`

: Transform to signed integer. Overflow possible if argument is outside of the

signed integer type range.

### Unsigned Integer Operators

`.&.`

: bit-wise and.`.|.`

: bit-wise or.`XOR`

: bit-wise xor.

### Transforming Between Different Unsigned Integer Types

Three functions are available: `toWord16`

, `toWord32`

and `toWord64`

. They all work for all unsigned integer types.

If the target type is smaller than the argument type, any excess bits will be truncated. The function `toWord64`

can

also be used to produce a `Word64`

from an `Int`

, which will have the same bit representation as the original `Int`

but

might have a different value.

### Random Numbers

`random :: () -> Double`

: Get a random number in the`[0,1)`

interval. Example where`x`

is assigned a random number between 0 and 10:`x := 10 * random() ;`

.

The`()`

argument is needed to make possible that each time`random`

is called a new random value can be generated.

### Trigonometrics

`pi :: Double`

. The Pi constant.`sin :: Double -> Double`

. Sine.`cos :: Double -> Double`

. Cosine.`tan :: Double -> Double`

. Tangent.