# Language Features

# Language Features

This page describes a few of the language features that Jactl offers. Jactl syntax is largely based on Java with parts also liberally borrowed from Groovy and Perl.

## Semi-Colon Optional

Semi-colon statement terminator is optional and is only needed if multiple statements are on the same line:

```
int x = 1
long y = 2
```

or

```
int x = 1; long y = 2
```

## Functions Return Value of Last Statement

Functions can explicitly return a value using the `return`

statement but if the last statement is not a `return`

then
its value will be returned:

```
int fib(int x) { return x <= 2 ? 1 : fib(x-1) + fib(x-2) }
```

is the same as:

```
int fib(int x) { x <= 2 ? 1 : fib(x-1) + fib(x-2) }
```

## Dynamic Typing with Optional Static Typing

Like Groovy, specifying types is optional in Jactl. Specifying a type will allow compile-time checking and some optimisations to be performed.

Static typing:

```
int fib(int x) { x <= 2 ? 1 : fib(x-1) + fib(x-2) }
```

Dynamic typing:

```
def fib(x) { x <= 2 ? 1 : fib(x-1) + fib(x-2) }
```

## Map and List Literals

Map and list literals:

```
// Lists
[1,2,3,4]
['abc', 'def', 'xyz']
// Maps (keys only need to be quoted if they are not simple identifiers)
[a:1, b:2, c:3]
['a key':'a value', b:'another value']
// Can easily return maps or lists from functions
def addAndMultiply(x,y) { [sum:x + y, product:x * y] }
```

Maps can use the canonical `[ ]`

form or can also be provided in JSON syntax:

```
Map m1 = [x:1, y:2]
Map m2 = {"x":1, "y":2}
```

## Interpolated Strings

Strings can be delimited with single quotes for simple strings, or double quotes for interpolated strings where `$`

is used to insert the value of a variable:

```
def x = 'this is a simple string'
def y = "Value of x = $x"
```

More complex expressions can be inserted into interpolated strings using `${ }`

:

```
def x = 3
def y = "x * x is ${x * x}"
// Expressions can contain other interpolated strings
def z = "Interpolated with ${"embedded interpolated (x*x=${x*x})"} string"
```

## Multi-Line Strings

Multi-line simple strings and interpolated strings can be specified using triple quotes:

```
def simple = '''
multi-line
simple string'''
def interpolated = """
This is not
a ${$simple}"""
```

Triple quotes are useful when you want to embed quotes and don’t want to have to escape them:

```
def x = 3
def y = """Value "${x*x}" for input of $x"""
def z = '''Triple quoted simple 'string' with embedded quotes'''
```

## Functions as Values

Functions are just objects and can be passed around as values:

```
def fib(x) { x <= 2 ? 1 : fib(x-1) + fib(x-2) }
def applyArg(f, x) { f(x) }
def g = fib // assign to another variable
g(40)
applyArg(fib, 40) // pass as arg to a function
```

Methods passed as value are bound to owning object:

```
class Multiplier {
int multiple = 1
def multiply(int x) { x * multiple }
}
def m = new Multplier(multiple:4)
g = m.multiply
g(3) // result is 12
applyArg(m.multiply, 5) // result is 20
```

## Closures

Closures in Jactl follow the Groovy syntax where the parameters are declared (with optional type) after the
initial `{`

:

```
def add = { x,y -> x + y }
add(2,3) // result is 5
def f = add
f('abc', 'xyz') // result is 'abcxyz'
```

Closures can close over variables visible in the existing scope:

```
def x = 2
def sumx = { y -> x + y }
sumx(3) // result is 5
x = 7
sumx(3) // result is 10
```

Variables can be modified within closures:

```
def x = 5
def incx = { x++ }
incx() // x will now be 6
```

## Closure Passing Syntax

If the last parameter to a function is a closure then the closure can be written after the closing `)`

for the other
arguments:

```
def logResult(prefix, clos) { "$prefix: result is ${ clos() }"}
logResult("Addition", { 2 + 3 }) // Pass closure as 2nd argument
logResult("Addition"){ 2 + 3 } // Closure arg can be provided after the ')'
```

If there are no other arguments then the parentheses are optional:

```
def fib(x) { x <= 2 ? 1 : fib(x-1) + fib(x-2) }
def measure(clos) { def start = nanoTime(); clos(); nanoTime() - start }
def totalTime
totalTime = measure{
for (int i = 0; i < 40; i++) {
fib(i + 1)
}
}
// Or using built-in "each" method
totalTime = measure{ 40.each{ i -> fib(i + 1) } }
```

## Regex Support with Capture Variables $1, $2, …

Regular expression matching is part of the language rather than being delegated to library calls.
The `=~`

operator performs searches a string for a substring that matches the given pattern:

```
'abcxdef' =~ /x/ // Result is true
```

Capture expressions in a regex pattern cause variables `$1`

, `$2`

, and so on to get populated with the corresponding
values from the string being matched:

```
if ('Total: 14ms' =~ /: (\d+)(.*)$/) {
println "Amount is $1, unit is $2" // Prints: Amount is 14, unit is ms
}
def x = 'a=1,bcd=234,e=3'
def result = []
// Extract all key=value values from x into list of [key,value] pairs:
while (x =~ /([^,=]+)=(\d+)/g) {
result <<= [$1, $2]
}
println result
// Prints: [['a', '1'], ['bcd', '234'], ['e', '3']]
```

Regex substitutions can be done using `s/.../.../`

syntax:

```
def x = 'abcdef'
x =~ s/[ace]/x/ // Substitute only first match
println x // Prints: xbcdef
x =~ s/[ace]/x/g // Replace all matching substrings
println x // Prints: xbxdxf
```

Embedded expressions can be used in the replacement string and can refer to capture variables:

```
def x = 'abcdef'
x =~ s/([ace])(.)/${ $2 + $1 }/g
println x // Prints: badcfe
```

## Implicit *it* Parameter

Closures that don’t declare a parameter have an automatic `it`

parameter created for them:

```
def twice = { it + it }
twice(3) // 6
// All numbers from 1 to 100 that are multiples of 7 and whose digits sum to be a multiple of 5:
def nums = 100.map{ it + 1 }
.filter{ it % 7 == 0 }
.map{ [it, it.toString().map{ it as int }.sum()] }
.filter{ it[1] % 5 == 0 }
.map{ it[0] }
// Result: [14, 28, 91]
```

Regular expressions operate on the implicit `it`

variable if no string value is provided:

```
// Sanitise text to make suitable for a link
def linkify = { s/ /-/g; s/[^\w-]//g }
// Find all top level headings in input and generate markdown for table of contents:
stream(nextLine).filter{ /^# /r }
.map{ s/# // }
.map{ "* [$it](#${ linkify(it.toLowerCase()) })" }
.each{ println it }
```

## Statement *if*/*unless* Condition

As well as standard `if`

statements, it is possible to have the `if`

and the condition come after the statement to
be executed so this standard form of `if`

:

```
def fib(x) {
if (x <= 2) {
return 1
}
return fib(x-1) + fib(x-2)
}
```

can be written:

```
def fib(x) {
return 1 if x <= 2
return fib(x-2) + fib(x-1)
}
```

It is also possible to us `unless`

instead of `if`

if that is more natural:

```
def fib(x) {
return 1 unless x > 2
return fib(x-2) + fib(x-1)
}
```

## Additional *and*, *or*, and *not* Operators

As well as the standard `&&`

, `||`

, and `!`

boolean operators, there are `and`

, `or`

, and `not`

operators that have
much lower precedence (lower than assignment expressions, for example).
This allows for this style of programming where it makes sense:

```
stream(nextLine).each{
/^\$ *cd +\/$/r and do { cwd = root; return }
/^\$ *cd +\.\.$/r and do { cwd = cwd.parent; return }
/^\$ *cd +(.*)$/r and do { cwd = cwd.children[$1]; return }
/^\$ *ls/r and return
/^dir +(.*)$/r and cwd.children[$1] = new Dir($1,-1,cwd)
/^(\d+) +(.*)$/n and cwd.children[$2] = new File($2,$1)
}
```

## Default Parameter Values

Functions and closures can have parameters with default values:

```
def format(num, int base = 10) { sprintf("%9s", num.toBase(base) as String) }
format(300) // Defaults to decimal: ' 300'
format(300, 16) // Hex: ' 12C'
format(300, 2) // Binary: '100101100'
```

## Named Arguments

When invoking fuctions/closures, the parameter names can be supplied:

```
def format(num, int base = 10) { sprintf("%9s", num.toBase(base) as String) }
format(num:400, base:16)
format(base:8, num:400) // any order supported for named args
```

*eval()* Statement

Jactl has a built-in `eval`

statement which will compile and execute a string of Jactl code at run-time.
A map of values for variables that the script references can be passed in as an optional argument:

```
eval('3 + 4') // returns 7
eval('x + y', [x:3, y:4]) // returns 7
eval('def twice = {it+it}; twice(x+y)', [x:3, y:4]) // returns 14
```

## Built-in JSON Support

Jactl can convert objects into JSON using the `toJson()`

method:

```
def x = [a:1,b:[x:3,y:4],c:['a','b','c']]
x.toJson() // {"a":1,"b":{"x":3,"y":4},"c":["a","b","c"]}
```

The corresponding `fromJson()`

method on strings will convert back from JSON to simple objects:

```
def json = '{"a":1,"b":{"x":3,"y":4},"c":["a","b","c"]}'
json.fromJson() // [a:1, b:[x:3, y:4], c:['a', 'b', 'c']]
```

For user defined classes, Jactl will generate a `toJson()`

method and a class static method `fromJson()`

:

```
class X { int i; String s }
X x = new X(i:3, s:'abc')
x.toJson() // {"i":3,"s":"abc"}
x = X.fromJson('{"i":3,"s":"abc"}')
```

## Decimal Number Support

Jactl uses `BigDecimal`

for “floating point” numbers by default (known as `Decimal`

numbers in Jactl).
This makes it suitable for manipulating currency amounts where arbitrary precision is required.
Jactl also offers `double`

numbers for situations where speed is more important than accuracy.
For example:

```
1211.12 / 100 // Result is: 12.1112
((double)1211.12) / 100 // Result is: 12.111199999999998
```

## Default Value Operator *?:*

Jactl offers the `?:`

operator that will return the value on the right-hand side if the left-hand side is null:

```
def x
x ?: 123 // value will be 123 since x is null
```

## Spaceship Operator *<=>*

The `<=>`

operator is a comparator operator that returns `-1`

, `0`

, or `1`

if the
left-hand side is less than, equal, or greater than the right-hand side respectively.
This makes it easy to create a comparator function for sorting things:

```
def employees = [[name:'Mary', salary:10000], [name:'Susan', salary:5000], [name:'Fred', salary:7000]]
// Sort according to salary
employees.sort{ a,b -> a.salary <=> b.salary }
// Result: [[name:'Susan', salary:5000], [name:'Fred', salary:7000], [name:'Mary', salary:10000]]
```

## Field Access *?.* and *?[* Operators

When accessing fields of a map or a list/array the `?.`

operator (for maps) and the `?[`

operator (for lists) allows
you to safely dereference a null value.
If the map/list is null then the resulting field reference will also be null if you use these operators:

```
def x
x.a // Generates a NullError since x is null
x?.a // Value will be null
x?[0] // Value will be null
x?.a?.b?[0] // Value will be null
```

## Auto-Creation of Fields

When assigning a value to a field expression like `a.b.c`

the values for intermediate fields will be automatically
constructed where possible.
For example:

```
def x = [:] // empty map
x.a.b.c = '123' // automatically create fields for x.a and x.a.b
x // x now has a value of: [a:[b:[c:'123']]]
x.d.e[2] = 'abc' // x now has a value of: [a:[b:[c:'123']], d:[e:[null, null, 'abc']]]
```

## Multi-Assignments

*(New feature: available on trunk.)*

Multiple variables can be declared and initialised in one statement and multiple assignments can also be done in one statement:

```
def (x,y,z) = [4,5,6] // initialisation
(x,y,z) = [0,1,2] // assignment
(x,y) += [2,3] // supports all assignment operators +=, -=, *=, etc
def a = [:]
(a.b.c[x], a.b.c[y]) = [x + y, y + z] // multi-assign with auto-creation of subfields
```

You can a multi-assignment to swap the values of two variables:

```
(x,y) = [y,x] // swaps x and y
```

## Classes

Users can define their own classes using a simplified syntax that is a subset of Java syntax:

```
class X {
int i
String field
def anotherField
def method() { field + ':' + anotherField + ':' + i }
}
class Y extends X {
// Override method in base class
def method() { "Y: " + super.method() }
}
def y = new Y(i:4, field:'value1', anotherField:'value2')
y.method() // returns 'Y: value1:value2:4'
```