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