Announcing Jactl 2.1.0
Jactl 2.1.0 is a new release with a handful of enhancements and bug fixes, as well as a brand new IntelliJ plugin.
Enhancements
IntelliJ Plugin
There is a new IntelliJ plugin available. Search for “Jactl” within Settings -> Plugins -> Marketplace of IntelliJ. It can also be downloaded and built from the source code at Jactl IntelliJ Plugin.
It offers the following features:
- Syntax colouring
- Error highlighting
- Intelligent indenting
- Code reformat
- Completions
- Show definition/show usages navigation
- Comment/uncomment selected code
- Rename refactoring
- Move file/move directory refactoring
- Paired brace matching
- Build/run/debug support
Postfix if/unless Enhancement (#58)
Previously it was possible to use if
or unless
after some statements such as:
x = 3 and return if y > 10
continue unless i == j
Now if
and unless
can be used after any expression.
For example:
def stats = [sum / cnt if cnt > 0, sum, cnt]
When the conditional part of the expression (the if
or unless
part) is false the result will be null
.
For Loops with Implicitly Created Variable (#61)
For loops can now use a loop variable without having to declare it:
for (i = 0; i < 10; i++) {
...
}
If the variable already exists within the scope, then it is reused.
If there was no variable of that name, it will be automatically declared (with appropriate type).
Furthermore, the variable will also be visible after the for
loop:
for (i = 0; i < 10; i++) {
break if test(i)
}
println "Found value that matches: $i"
Variables that have an explicit declaration only have visibility within the for loop itself:
for (int i = 0; i < 10; i++) {
break if test(i)
}
// i is not visible here
Allow null Dereference in Left-Hand Side of ?: Operator (#69)
The ?:
operator is used to provide a default value when the left-hand side is null
:
name = x.info.name ?: 'no name provided'
If x.info.name
returns null, then 'no name provided'
is used as the value instead.
To deal with situations where x
or x.info
could also be null, you would previously have to write:
name = x?.info?.name ?: 'no name provided'
The ?:
operator has now been enhanced to cater for these situations and will return the right-hand value if
a null
dereference would have occurred on the left-hand side.
This now works if x
, x.info
, or x.info.name
are null
:
name = x.info.name ?: 'no name provided'
Support Binding Variables for “*” in List Patterns of Switch Statements (#70)
In Jactl, the switch
statement (which is actually an expression since it evaluates to a result) supports matching
against patterns that can be simple literal values (as in a traditional switch
) or can match based on type and
structure.
In particular, list patterns can be used to match lists based on the list size and list content.
For example:
switch (x) {
[_] -> 'list with single element'
[int,int] -> 'list of two ints'
[String,*] -> 'list beginning with a String'
}
It is possible to supply the name of a binding variable to bind to specific elements which can then be used
within that switch
case:
switch (x) {
[a] -> "list of $a"
[head,*] -> "first element is $head"
}
The *
wildcard is used to match any number elements but up until now, could not be bound to a binding variable.
Now it is possible to bind this to a variable which will then be a list of those elements.
For example, a simple quick sort:
def qsort(x) {
switch (x) {
[], [_] -> x
[h,*t] -> qsort(t.filter{ it < h }) + h + qsort(t.filter{ it >= h})
}
}
Other Enhancements
- #62 Allow implicit creation of top level variables when auto-creating fields during an assignment
- #64 Allow ‘=’ on separate line to variable declaration
- #65 Allow global variables to also be accessible from within class methods
Bug Fixes
- #59 Parser bug where identifier immediately on next line after break/continue was being treated as a loop label
- #60 ArrayIndexOutOfBounds exception from ASM library for certain values in action of a switch case
- #63 Compile error if first statement in a no-arg closure is a multi-variable declaration
- #65 Fix issue with creation of an alias when registering a global function under multiple names
- #68 Class verification error when using ?: operator in some scenarios