// GROOVYRS — LANGUAGE REFERENCE

groovyrs v0.1.9 · Groovy on fusevm · lex/parse → AST → bytecode → Cranelift JIT · no bespoke VM, no JVM · MIT · in active development

Docs GitHub
// Color scheme

>_LANGUAGE REFERENCE

Every name the current groovyrs build recognizes — reserved words, literal forms, contextual keywords, operators, the script print commands, every dispatched GDK method, every readable property, the class-member hooks the runtime calls by name, the modeled throwable hierarchy, and the built-in type names instanceof answers for — each with a signature, a description, and a runnable example. This page is generated from the language-server corpus (src/lsp.rs) by the gen-docs binary, so it stays in sync with what the runtime and editor tooling actually know about. Reserved words and literal forms mirror lexer.rs, contextual keywords and operators mirror parser.rs and compiler.rs, and every method, property, hook, throwable, and type name mirrors a real dispatch arm in src/host.rs or a real table in src/throwable.rs / src/compiler.rs. Where groovyrs diverges from Apache Groovy the entry says so.

Reserved Words

# def

def name [= expr]

Declare a dynamically-typed local variable, or a script binding at top level. An unassigned `def` reads as `null`.

def x = 5
println(x)   // => 5

# if

if (cond) stmt

Conditional branch. The condition goes through Groovy truth (`host::groovy_truthy`), so an empty string, an empty list, `0`, and `null` are all false.

if (1 < 2) println("yes")   // => yes

# else

if (cond) stmt else stmt

The fallback branch of an `if`. Binds to the nearest unmatched `if`.

if (false) println("a") else println("b")   // => b

# while

while (cond) stmt

Loop while the condition is Groovy-truthy; the test runs before the first iteration.

def i = 0
while (i < 3) i++
println(i)   // => 3

# do

do stmt while (cond)

Post-tested loop: the body runs once before the condition is evaluated.

def i = 0
do { i++ } while (i < 3)
println(i)   // => 3

# for

for (init; cond; update) stmt | for (name in iterable) stmt

C-style three-clause loop, or the `in` form over a range, list, map, or String. The `in` form materialises its subject through `host::GITER` first.

for (i in 0..2) print(i)   // => 012

# in

for (name in iterable)

The iteration separator of a `for` loop. Walks a range's integers, a list's elements, a map's entries, or a String's characters.

for (n in [1, 2, 3]) print(n)   // => 123

# switch

switch (subject) { case label: … default: … }

Multi-way branch, in statement or value position. Each label is tested with Groovy's `isCase` (`host::GIS_CASE`), so a constant, a range, a list, a type name, a `~/…/` pattern, or a closure all work as labels. As an expression it answers the matching arm, and `null` when none matched — no exhaustiveness is required.

switch (5) { case 4..6: println("in"); break; default: println("out") }   // => in

# case

case label[, label…]: | case label[, label…] -> value

One `switch` label, or several sharing an arm. A colon section falls through to the next until a `break`; an arrow arm runs alone and is valued by its trailing expression, so a braced body is a block (`case 2 -> { 5 }` is 5). The two forms may not be mixed in one `switch`. A COMMA list is switch-expression grammar: writing `case 3, 4:` makes the whole switch an expression, so every arm must yield or throw and `break` is no longer one of its statements.

println(switch (1) { case 1, 2 -> "low"; default -> "hi" })   // => low

# yield

yield expr

The value of the enclosing `switch` expression, which it also leaves — running any `finally` it jumps over. Contextual: only inside a `switch` arm is `yield` this statement, so a program may still use the name elsewhere.

println(switch (1) { case 1: yield "one"; default: yield "d" })   // => one

# default

default: | default ReturnType method(params) { … }

The `switch` section entered when no `case` label matched; a duplicate `default` is a parse error. Inside an `interface` body it instead marks a method that carries an implementation.

switch (9) { case 1: break; default: println("d") }   // => d

# assert

assert cond [ : message ]

Raise `PowerAssertionError` when the condition is falsy, printing the source text with each recorded sub-expression's value under the column it occupied. The `: message` form instead raises a plain `AssertionError` whose text is `<message>. Expression: <condition>`, with a `Values:` clause naming the condition's bare-variable operands.

assert 1 + 1 == 2   // passes silently

# return

return [expr]

Return from the enclosing method, closure, or constructor; at script level it ends the script. A method with no `return` yields its last expression.

def f() { return 7 }
println(f())   // => 7

# break

break [label]

Exit the nearest enclosing loop or `switch`. `break label` exits the loop carrying that label; an unknown label is a compile error.

for (i in 0..9) { if (i == 3) break; print(i) }   // => 012

# continue

continue [label]

Skip to the next iteration of the nearest enclosing loop, or of the labeled one.

for (i in 0..3) { if (i == 1) continue; print(i) }   // => 023

# new

new Type([args])

Construct an instance. Fields across the whole superclass chain are materialised to `null`, initialisers run superclass-first, then the most-derived constructor of matching arity runs (`host::GNEW`).

class P { def x; P(v) { x = v } }
println(new P(7).x)   // => 7

Literals and Literal Forms

# true

true

The boolean true value, lexed as a reserved word.

println(true)   // => true

# false

false

The boolean false value.

println(1 > 2)   // => false

# null

null

The null reference. A method call on `null` answers only `toString`, `equals`, and `getClass`; anything else raises `NullPointerException`.

def x
println(x)   // => null

# 42

<digits>

An integer literal. It is an `Integer` when it fits in 32 bits and a `Long` when it does not, which is the width its arithmetic wraps at: `1000000 * 1000000` is `-727379968`. A literal past `Long` range is a lex error, not a silent wrap.

println(42 + 1)   // => 43

# 42L

<digits>L | <digits>l

A long-suffixed integer literal. The suffix makes the literal a `Long` whatever its magnitude, so its arithmetic wraps at 64 bits rather than 32: `2000000000L + 2000000000L` is `4000000000` where the unsuffixed form is `-294967296`.

println(2000000000L + 2000000000L)   // => 4000000000

# 0xFF

0x<hex> | 0b<binary> | 0<octal>

A radix-prefixed integer literal. The digits are read as a magnitude and the literal takes the smallest type holding it, so `0xff` is an `Integer` and `0xFFFFFFFF` is the `Long` `4294967295`.

println(0xFF)   // => 255

# 1_000_000

<digits>_<digits>

An integer or decimal literal with `_` group separators. The separators are not part of the value.

println(1_000_000)   // => 1000000

# 1.50

<digits>.<digits> [g|G]

An unsuffixed decimal literal — a `java.math.BigDecimal`, kept as its exact source text so the literal's own scale survives. `1.50` prints with both fraction digits, which no `f64` could preserve.

println(1.50)   // => 1.50

# 1.5d

<digits>.<digits> d|D|f|F

A `d`/`f`-suffixed literal — an IEEE double (`Value::Float`). This is the only literal form that is *not* a `BigDecimal`. The `instanceof` table classifies by value shape rather than by literal form, so groovyrs answers `1.5d instanceof BigDecimal` true and `1.5 instanceof BigDecimal` false — Apache Groovy answers the opposite for both.

println(1.5d / 0.0d)   // => Infinity

# 2.5e7

<digits>[.<digits>]e[+|-]<digits>

An exponent literal. The exponent makes the literal a decimal with or without a fractional part, and the result is a `BigDecimal` unless a `d`/`f` suffix follows.

println(2.5e7)   // => 2.5E+7

# 'text'

'…'

A single-quoted String — inert text. Backslash escapes are decoded, but `$` never interpolates.

println('a$b')   // => a$b

# "text"

"… $name … ${ expr } …"

A double-quoted String. With no placeholder it lexes as a plain String; with one it becomes a GString whose parts render through `toString()` at runtime (`host::GSTRING`). `$name` takes a dotted *property* path only, so `"$n.toString()"` reads the property `n.toString` and leaves `()` as text.

def n = 5
println("n=$n and ${n + 1}")   // => n=5 and 6

# ~/pattern/

~/…/

A regex literal — a pattern handle built by `host::GREGEX`. Only `\/` is special inside the delimiters; every other backslash escape passes through to the pattern. Modeled far enough to drive a `switch` label, whose `isCase` is a *full* match.

switch ("abc") { case ~/a.c/: println("m"); break }   // => m

# [a, b]

[expr, …]

A list literal. Lists are `Value::Array` and print with Groovy's `[1, 2, 3]` rendering.

println([1, 2, 3])   // => [1, 2, 3]

# []

[]

The empty list. Falsy under Groovy truth, so `if ([])` does not run.

if (![]) println("empty")   // => empty

# [k: v]

[key: expr, …] | [(expr): expr, …]

A map literal, built by `host::GMAKE_MAP` into an insertion-ordered map. A bare identifier key is the string of its own name; parenthesise the key to compute it.

println([a: 1, b: 2])   // => [a:1, b:2]

# [:]

[:]

The empty map — the form that distinguishes an empty map from the empty list `[]`.

println([:].isEmpty())   // => true

# { a, b -> … }

{ param, … -> body }

A closure literal with an explicit parameter list, including the zero-parameter form `{ -> … }`. It lowers to a subroutine region plus a runtime closure handle and captures the enclosing script bindings.

def add = { a, b -> a + b }
println(add(2, 3))   // => 5

# { it }

{ body }

A closure literal with no parameter list: Groovy supplies the single implicit parameter `it`.

println([1, 2, 3].collect { it * 2 })   // => [2, 4, 6]

Contextual Keywords

# try

try { … } catch (T e) { … } [finally { … }]

Run a block with `catch` handlers and/or a `finally` cleanup. Recognised at statement start when followed by `{`; a `try` with neither a `catch` nor a `finally` is a parse error.

try { throw new Exception("x") } catch (Exception e) { println(e.message) }   // => x

# catch

catch (Type [| Type…] name) { … }

Handle a thrown value whose class chain reaches the named type. The multi-catch form `catch (A | B e)` tests each alternative in turn.

try { 1 / 0 } catch (ArithmeticException e) { println(e.message) }   // => Division by zero

# finally

finally { … }

Cleanup block. Runs on every exit path out of the `try` — normal completion, a caught throwable, an uncaught one, and a `return`.

try { println("body") } finally { println("cleanup") }   // => body then cleanup

# throw

throw expr

Raise a throwable (`host::GTHROW`), unwinding to the innermost `catch` whose type matches. groovyrs has no unwind opcode: the throw parks the value and the compiler-emitted guards cut the stack back.

try { throw new IllegalStateException("bad") } catch (Exception e) { println(e) }   // => java.lang.IllegalStateException: bad

# class

[modifiers] class Name [extends S] [implements I, …] { … }

Declare a class. Registration is a `host::GCLASS` call carrying the superclass, interfaces, field names, initialisers, methods, and constructors.

class P { def x = 1 }
println(new P().x)   // => 1

# interface

[modifiers] interface Name [extends I, …] { … }

Declare an interface. `new` on one faults; its `default` methods are reachable from an implementing class through the interface closure.

interface Greet { default def hi() { "hi" } }
class A implements Greet {}
println(new A().hi())   // => hi

# extends

class Name extends Superclass

Name the direct superclass in a class header (or the supertype list in an interface header). Method lookup walks this chain most-derived first.

class A { def f() { 1 } }
class B extends A {}
println(new B().f())   // => 1

# implements

class Name implements Iface, …

Name the interfaces a class implements. They participate in `instanceof` and in `catch` matching through the interface closure.

interface I {}
class A implements I {}
println(new A() instanceof I)   // => true

# instanceof

value instanceof Type

Runtime type test (`host::GINSTANCEOF`) at relational precedence. Answers for declared classes, the 25 modeled throwables, and the 22 built-in type names. `null instanceof X` is always false.

println("x" instanceof String)   // => true

# package

package name.path

A leading package declaration. Parsed and skipped — groovyrs has no package namespace, so the line has no runtime effect.

package demo
println(1)   // => 1

# import

import name.path[.*]

A leading import. Parsed and skipped: the built-in type and throwable names resolve without one, and there is no classpath to import from.

import java.util.List
println([1].size())   // => 1

# public

public …

Access modifier on a type declaration or class member. Accepted and skipped — groovyrs enforces no access control.

class A { public def f() { 1 } }
println(new A().f())   // => 1

# private

private …

Access modifier. Accepted and skipped: a `private` member is reachable from anywhere, which is a deliberate divergence from Groovy.

class A { private def x = 1 }
println(new A().x)   // => 1

# protected

protected …

Access modifier. Accepted and skipped, like `private`.

class A { protected def x = 2 }
println(new A().x)   // => 2

# static

static …

Member modifier. Accepted and skipped — groovyrs has no static storage, so a `static` field is an ordinary per-instance field.

class A { static def f() { 3 } }
println(new A().f())   // => 3

# final

final …

Immutability modifier. Accepted and skipped; groovyrs does not reject a later write.

class A { final def x = 4 }
println(new A().x)   // => 4

# abstract

abstract …

Abstractness modifier. Accepted and skipped: groovyrs does not refuse `new` on an `abstract` class the way it refuses it on an `interface`.

abstract class A { def f() { 5 } }
println(new A().f())   // => 5

# synchronized

synchronized …

Member modifier. Accepted and skipped — a groovyrs script runs on one thread, so there is nothing to synchronise.

class A { synchronized def f() { 6 } }
println(new A().f())   // => 6

# transient

transient …

Serialization modifier. Accepted and skipped; groovyrs has no serialization.

class A { transient def x = 7 }
println(new A().x)   // => 7

# volatile

volatile …

Memory-visibility modifier. Accepted and skipped, like `synchronized`.

class A { volatile def x = 8 }
println(new A().x)   // => 8

# this

this[.member]

The receiver inside a class member. A bare field or method name inside a class body already resolves through `this`, so the explicit form is rarely needed.

class A { def x = 1; def f() { this.x } }
println(new A().f())   // => 1

# super

super(args) | super.method(args)

Invoke the superclass constructor (`host::GSUPER_CTOR`, valid as the first statement of a constructor) or a superclass method non-virtually (`host::GSUPER_METHOD`).

class A { def f() { "A" } }
class B extends A { def f() { super.f() + "B" } }
println(new B().f())   // => AB

# it

it

The implicit single parameter of a closure written without a parameter list. It is an ordinary binding, so a closure with explicit parameters has no `it`.

println([1, 2].collect { it + 1 })   // => [2, 3]

Operators

# +

a + b

Addition. Two integers stay integral, a decimal operand keeps `BigDecimal` scale, a `double` operand promotes to a double, a String operand concatenates (rendering the other side through `toString()`), a list operand appends, and a map operand merges. On a class instance it dispatches `plus`.

println(1 + 2)
println("a" + 1)   // => 3 then a1

# -

a - b

Subtraction over the same numeric tower as `+`. On a class instance it dispatches `minus`.

println(5 - 2)   // => 3

# *

a * b

Multiplication. On a class instance it dispatches `multiply`.

println(3 * 4)   // => 12

# /

a / b

Division, lowered to the `GDIV` builtin rather than a VM opcode. Two integers that divide exactly yield an integer; anything else yields a `BigDecimal` — never a double, unless an operand already is one. A zero divisor raises `ArithmeticException`. On a class instance it dispatches `div`.

println(7 / 2)
println(4 / 2)   // => 3.5 then 2

# %

a % b

Remainder. The compiler emits a bare opcode when it can prove the divisor non-zero and a guarded builtin call otherwise. On a class instance it dispatches `remainder`, which is Groovy's mapping — not `mod`.

println(5 % 3)   // => 2

# - (unary)

-a

Arithmetic negation. On a class instance it dispatches `negative`.

println(-(-3))   // => 3

# !

!a

Logical negation of the operand's Groovy truth, so `![]`, `!""`, `!0`, and `!null` are all true.

println(!false)   // => true

# ==

a == b

Equality. Groovy's `==` is value equality, not identity, and it is null-safe. On a class instance it uses `compareTo(…) == 0` when the class defines `compareTo`, a user `equals` otherwise, and heap identity with neither.

println([1, 2] == [1, 2])   // => true

# !=

a != b

The negation of `==`, resolved through the same instance rules.

println(1 != 2)   // => true

# <

a < b

Less-than. On a class instance it dispatches `compareTo`; a class without one falls back to comparing the rendered strings.

println(1 < 2)   // => true

# >

a > b

Greater-than, resolved like `<`.

println(3 > 2)   // => true

# <=

a <= b

Less-than-or-equal, resolved like `<`.

println(2 <= 2)   // => true

# >=

a >= b

Greater-than-or-equal, resolved like `<`.

println(2 >= 3)   // => false

# <=>

a <=> b

The spaceship operator — `compareTo`, lowered to the `GCMP` builtin. Yields a negative number, zero, or a positive number.

println(5 <=> 3)   // => 1

# &&

a && b

Short-circuiting logical and. Both operands go through Groovy truth; the result is a `Boolean`, not the operand.

println(1 && "x")   // => true

# ||

a || b

Short-circuiting logical or, with the same truth rules as `&&`.

println(0 || 3)   // => true

# ?:

a ?: b

The Elvis operator. Evaluates the left side once and yields it when Groovy-truthy, otherwise the right side. Unlike `||` it yields the *operand*, not a `Boolean`.

println(null ?: "d")
println("" ?: "d")   // => d then d

# ? :

cond ? a : b

The ternary conditional. The condition goes through Groovy truth.

println(true ? 1 : 2)   // => 1

# ?.

recv?.member | recv?.method(args)

Safe navigation. Yields `null` without dispatching when the receiver is `null`, so a chain of `?.` never raises `NullPointerException`.

def x
println(x?.size())   // => null

# *.

recv*.member | recv*.method(args)

The spread-dot operator. Desugars to `recv.collect { it?.member }` — including the safe navigation, which is why a `null` element spreads to `null` rather than raising.

println([1, 2, 3]*.toString())   // => [1, 2, 3]

# [ ]

recv[index]

Subscript, lowered to the `GINDEX` builtin. A list index past the end yields `null` and a negative index counts from the end; a String subscript yields a one-character String and raises past the end; a map subscript is a key read. On a class instance it dispatches `getAt`.

println([1, 2, 3][-1])
println("hello"[1])   // => 3 then e

# ..

a..b

An inclusive range — a `groovy.lang.Range` object, so `(0..3).class.simpleName` is `IntRange` and printing one shows `0..3`. Being a `java.util.List` in Groovy, every list method and operator applies to it as well.

println(0..3)   // => [0, 1, 2, 3]

# ..<

a..<b

A half-open range: the endpoint is excluded, so `(0..<3)` enumerates `0, 1, 2` and prints `0..<3`.

println(0..<3)   // => [0, 1, 2]

# ++

x++ | ++x

Increment. Postfix yields the value before the update, prefix the value after. On a class instance it dispatches `plus` — not Groovy's `next`, which is a deliberate divergence.

def i = 0
println(i++)
println(i)   // => 0 then 1

# --

x-- | --x

Decrement, mirroring `++`; on a class instance it dispatches `minus` rather than Groovy's `previous`.

def i = 2
println(--i)   // => 1

# =

target = expr

Assignment to a local, a script binding, a field, a property (`GSETPROP`), or a subscript. A property write to a name the class chain never declared raises `MissingPropertyException` rather than growing the object.

def x = 1
x = 2
println(x)   // => 2

# +=

target += expr

Compound addition — the `+` lowering with a load before and a store after. Inside a class body a bare field target routes through the property builtins.

def x = 1
x += 2
println(x)   // => 3

# -=

target -= expr

Compound subtraction.

def x = 5
x -= 2
println(x)   // => 3

# *=

target *= expr

Compound multiplication.

def x = 3
x *= 4
println(x)   // => 12

# /=

target /= expr

Compound division. Reuses the `GDIV` builtin, so `7 /= 2` yields a `BigDecimal`.

def x = 7
x /= 2
println(x)   // => 3.5

# %=

target %= expr

Compound remainder, reusing the guarded `%` lowering.

def x = 7
x %= 3
println(x)   // => 1

# .

recv.member | recv.method(args)

Member access. A call routes through the `GMETHOD` builtin and a read through `GPROP`; on a class instance a read prefers a user `getX()` getter over the raw field.

println("abc".size())   // => 3

# ->

{ params -> body }

The closure parameter separator. Its presence — even with an empty list — is what makes the parameter list explicit and suppresses the implicit `it`.

println([1, 2].collect { n -> n * 10 })   // => [10, 20]

# |

catch (A | B name)

The multi-catch alternative separator. This is its only meaning: groovyrs has no bitwise-or operator.

try { 1 / 0 } catch (IOException | ArithmeticException e) { println("caught") }   // => caught

# @

@Name

The annotation marker. Annotations are lexed and skipped before a class or member declaration; groovyrs attaches no meaning to any of them.

class A { @Override def toString() { "A" } }
println(new A())   // => A

# label:

name: loop

A statement label. Only a loop is a useful target, and only `break label` / `continue label` read it; naming a label no enclosing loop carries is a compile error.

outer: for (i in 0..3) { if (i == 1) break outer; print(i) }   // => 0

Script Commands

# println

println([value])

Print a Groovy-formatted value and a trailing newline, then yield `null`. Parentheses are optional. A class instance renders through its `toString()`; a list renders as `[1, 2, 3]` and a map as `[a:1]`.

println("hi")
println([a: 1])   // => hi then [a:1]

# print

print([value])

Print a Groovy-formatted value with no trailing newline, then yield `null`.

print("a"); print("b")   // => ab

GDK — Any Receiver

# size

value.size() -> Integer

The element count: characters for a String, elements for a list, entries for a map. groovyrs answers `size()` on *every* receiver and yields `0` for a scalar, where Groovy raises `MissingMethodException` on, say, an `Integer`.

println("ab".size())
println([1, 2, 3].size())   // => 2 then 3

# getClass

value.getClass() -> Class

The `java.lang.Class` handle for the receiver, answered before the per-type table so it works on everything. `null.getClass()` answers `NullObject`'s class rather than raising, as in Groovy.

println(1.5.getClass().getName())   // => java.math.BigDecimal

# toString

value.toString() -> String

The receiver rendered the way `println` renders it. A class instance uses its own `toString()` when it declares one; a throwable renders as `qualified.Name: message`.

println([1, 2].toString())   // => [1, 2]

# equals

value.equals(other) -> Boolean

Value equality. Modeled explicitly only on a `null` receiver (`null.equals(null)` is true); on a class instance a user `equals` is dispatched, and on other receivers it is the `==` comparison.

def x
println(x.equals(null))   // => true

# call

closure.call([args]) -> Object

Invoke a closure explicitly. `clo(args)` and `clo.call(args)` reach the same code path; calling `call` on a non-closure falls through to ordinary GDK dispatch.

def f = { a -> a * 2 }
println(f.call(4))   // => 8

GDK — String

# length

string.length() -> Integer

The character count of a String — Unicode characters, not bytes.

println("héllo".length())   // => 5

# toUpperCase

string.toUpperCase() -> String

The receiver uppercased. Uses Rust's Unicode-aware case mapping rather than a JDK locale.

println("abc".toUpperCase())   // => ABC

# toLowerCase

string.toLowerCase() -> String

The receiver lowercased.

println("ABC".toLowerCase())   // => abc

# trim

string.trim() -> String

The receiver with leading and trailing whitespace removed.

println("  x  ".trim() + "!")   // => x!

# reverse

string.reverse() -> String

The receiver's characters in reverse order.

println("abc".reverse())   // => cba

# isEmpty

string.isEmpty() -> Boolean

True when the String has no characters. This is a length test, not a whitespace test.

println("".isEmpty())   // => true

# contains

string.contains(needle) -> Boolean

True when the rendered argument occurs as a substring. The argument is rendered first, so a non-String argument is compared by its printed form — where Apache Groovy raises `MissingMethodException` for one.

println("abc".contains("bc"))   // => true

# toInteger

string.toInteger() -> Integer

Parse the *trimmed* text as an integer, range-checked to 32 bits. A failed parse — including one that overflows `Integer` — raises `NumberFormatException` naming the text.

println("42".toInteger() + 1)   // => 43

# toLong

string.toLong() -> Long

Parse the trimmed text as a 64-bit integer. Unlike `toInteger` it accepts values outside the 32-bit range.

println("2147483648".toLong())   // => 2147483648

# toDouble

string.toDouble() -> Double

Parse the text the way `Double.parseDouble` does: surrounding whitespace and a trailing `d`/`f` are allowed, as are `Infinity` and `NaN` — but not Rust's extra `inf`/`nan` spellings or a hex literal.

println("1.5".toDouble() * 2)   // => 3.0

# toFloat

string.toFloat() -> Float

Identical to `toDouble` — groovyrs has one IEEE type, so there is no narrowing to 32-bit precision.

println("0.25".toFloat())   // => 0.25

# toBigDecimal

string.toBigDecimal() -> BigDecimal

`new BigDecimal(text.trim())`. The failure carries `BigDecimal`'s own character-level diagnostics, including the message-less `NumberFormatException` an empty string produces.

println("1.50".toBigDecimal())   // => 1.50

GDK — List

# isEmpty

list.isEmpty() -> Boolean

True when the list has no elements.

println([].isEmpty())   // => true

# contains

list.contains(value) -> Boolean

True when some element matches. groovyrs compares the *rendered* forms, so `[1, 2].contains("1")` is true where Groovy's `equals`-based test is false.

println([1, 2, 3].contains(2))   // => true

# get

list.get(index) -> Object

The element at `index`. Unlike the `[i]` subscript this is the raw JDK call: any out-of-range index raises `IndexOutOfBoundsException` instead of yielding `null`, and a negative index does not wrap.

println([10, 20].get(1))   // => 20

# reverse

list.reverse() -> List

A new list with the elements in reverse order. The receiver is not mutated.

println([1, 2, 3].reverse())   // => [3, 2, 1]

# join

list.join([separator]) -> String

Render each element the way `println` does and join them with the separator, which defaults to the empty string.

println([1, 2, 3].join("-"))   // => 1-2-3

# subList

list.subList(from, to) -> List

A live `java.util.ArrayList$SubList` window over `[from, to)` — a second reference to part of the backing list, not a copy. Writes travel both ways, and a structural write through the window splices the backing list and resizes the window. A structural change made to the backing list through any *other* reference invalidates the window: every later use raises `ConcurrentModificationException`. Bounds are the JDK's, checked in the JDK's order. On a `Range` the answer is another `Range`.

def a = [1, 2, 3, 4]; def s = a.subList(1, 3); s.set(0, 99); println(a)   // => [1, 99, 3, 4]

# addAll

list.addAll(collection) | list.addAll(index, collection) -> Boolean

Append the collection's elements, or insert them at `index`. Answers whether the list changed, so an empty argument is `false`.

def a = [1, 2]; a.addAll(1, [8, 9]); println([a, a.addAll([])])   // => [[1, 8, 9, 2], false]

GDK — List Closure Methods

# each

list.each { it -> … } -> List

Run the closure once per element for its side effects and yield the receiver. A `throw` inside the closure stops the iteration so the exception reaches the caller's handler.

[1, 2].each { print(it) }   // => 12

# eachWithIndex

list.eachWithIndex { it, i -> … } -> List

Like `each`, but the closure also receives the element's 0-based index.

[9, 8].eachWithIndex { v, i -> println("$i:$v") }   // => 0:9 then 1:8

# collect

list.collect { it -> … } -> List

Map each element through the closure into a new list of the same length.

println([1, 2, 3].collect { it * 2 })   // => [2, 4, 6]

# findAll

list.findAll { it -> … } -> List

Keep the elements for which the closure's result is Groovy-truthy.

println([1, 2, 3].findAll { it > 1 })   // => [2, 3]

# find

list.find { it -> … } -> Object

The first element the closure accepts, or `null` when none does.

println([1, 2, 3].find { it > 1 })   // => 2

# inject

list.inject([seed]) { acc, val -> … } -> Object

Fold left. With a seed the fold starts there; without one it seeds with the first element and starts at the second. An empty list with no seed yields `null`.

println([1, 2, 3].inject(0) { a, b -> a + b })   // => 6

# sum

list.sum([seed]) | list.sum { it -> … } -> Object

Add the elements, or the closure's results, using the `+` numeric tower — so a String element concatenates and a list element appends. An empty list with no seed yields `null`.

println([1, 2, 3].sum())
println([1, 2, 3].sum { it * 2 })   // => 6 then 12

# sort

list.sort([true]) | list.sort { it -> key } | list.sort { a, b -> … } -> List

Order the elements naturally, by a one-parameter key closure, or by a two-parameter comparator. The sort is in place and answers the receiver, so every other name for that list sees it — whether the receiver is a variable, a map value, or an element of another list. `sort(false)` asks for a copy and leaves the receiver alone.

println([3, 1, 2].sort())   // => [1, 2, 3]

# unique

list.unique([true]) | list.unique { it -> key } -> List

Drop later duplicates, keeping source order. Comparison is the same natural-or-closure ordering `sort` uses, and it mutates the receiver in place the same way. `unique(false)` asks for a copy.

println([1, 1, 2].unique())   // => [1, 2]

# max

list.max([{ it -> key }]) -> Object

The greatest element, by natural order or by the closure's key.

println([3, 1, 2].max())   // => 3

# min

list.min([{ it -> key }]) -> Object

The least element, by natural order or by the closure's key.

println([3, 1, 2].min())   // => 1

# groupBy

list.groupBy { it -> key } -> Map

A map from each closure result to the sublist of elements that produced it, with keys in first-seen order. The key is the rendered form of the closure's result.

println([1, 2, 3].groupBy { it % 2 })   // => [1:[1, 3], 0:[2]]

# countBy

list.countBy { it -> key } -> Map

A map from each closure result to how many elements produced it, keys in first-seen order.

println([1, 2, 3, 4].countBy { it % 2 })   // => [1:2, 0:2]

# findIndexValues

list.findIndexValues([from]) { it -> … } -> List

Every index whose element the closure accepts, where `findIndexOf` answers only the first. An optional leading argument is the index to start from.

println([1, 2, 3].findIndexValues { it > 1 })   // => [1, 2]

GDK — Closure

# curry

closure.curry(args…) -> Closure

A closure with `args` bound to the *leading* parameters; the rest are supplied at call time.

def add = { a, b -> a + b }
println add.curry(1)(2)   // => 3

# rcurry

closure.rcurry(args…) -> Closure

As `curry`, but the bound values go at the *end* of the supplied arguments.

def sub = { a, b -> a - b }
println sub.rcurry(1)(5)   // => 4

# ncurry

closure.ncurry(n, args…) -> Closure

As `curry`, but the bound values are spliced in at index `n` of the argument list.

def three = { a, b, c -> "$a$b$c" }
println three.ncurry(1, "X")("a", "c")   // => aXc

# memoize

closure.memoize() -> Closure

A closure that runs the body once per distinct argument list and answers the recorded result thereafter. The cache is keyed by the rendered arguments and shared by every holder of the handle.

def n = 0
def f = { n++; it * 2 }.memoize()
f(3); f(3)
println n   // => 1

# andThen

closure.andThen(other) -> Closure

Composition running the receiver first and feeding its result to `other` — the same as Groovy's `>>`.

def inc = { it + 1 }
println inc.andThen { it * 2 }(3)   // => 8

# compose

closure.compose(other) -> Closure

Composition running `other` first — the same as Groovy's `<<`.

def inc = { it + 1 }
println inc.compose { it * 2 }(3)   // => 7

GDK — Map

# isEmpty

map.isEmpty() -> Boolean

True when the map has no entries.

println([:].isEmpty())   // => true

# containsKey

map.containsKey(key) -> Boolean

True when the map holds the rendered key.

println([a: 1].containsKey("a"))   // => true

# get

map.get(key) -> Object

The value stored under the rendered key, or `null` when absent. Unlike `List.get` this never raises.

println([a: 1].get("a"))   // => 1

# keySet

map.keySet() -> List

The keys in insertion order. groovyrs yields a plain list rather than a `Set` view, so it is ordered and allows subscripting.

println([a: 1, b: 2].keySet())   // => [a, b]

# keys

map.keys() -> List

A groovyrs synonym for `keySet`. Apache Groovy has no `Map.keys()` — this is an extension, not a port.

println([a: 1, b: 2].keys())   // => [a, b]

# values

map.values() -> List

The values in insertion order, as a plain list.

println([a: 1, b: 2].values())   // => [1, 2]

# subMap

map.subMap(keys) | map.subMap(k1, k2, …) -> Map

The entries for the listed keys, in the *receiver's* order. A key the map does not hold is dropped rather than read as `null`.

println([a: 1, b: 2].subMap(["a"]))   // => [a:1]

# spread

map.spread() -> Map

A shallow copy of the map.

println([a: 1].spread())   // => [a:1]

# intersect

map.intersect(other) -> Map

The entries `other` holds identically — same key *and* same value. `map.minus(other)` (`map - other`) is the complement.

println([a: 1, b: 2].intersect([a: 1]))   // => [a:1]

# iterator

map.iterator() -> Iterator

A live cursor over the map's entries. Also defined on a list, a range and a `String`; `next()` advances the shared handle and raises `NoSuchElementException` past the end.

println([a: 1].iterator().next())   // => a=1

GDK — Map Closure Methods

# each

map.each { k, v -> … } | map.each { entry -> … } -> Map

Run the closure once per entry and yield the receiver. A two-parameter closure receives `(key, value)`; a one-parameter closure receives one `Map.Entry` — the closure's declared parameter count decides, as in Groovy.

[a: 1, b: 2].each { k, v -> println("$k$v") }   // => a1 then b2

# eachWithIndex

map.eachWithIndex { k, v, i -> … } -> Map

Like `each`, with the entry's 0-based index appended to the closure's arguments.

[a: 1].eachWithIndex { k, v, i -> print("$i:$k") }   // => 0:a

# collect

map.collect { k, v -> … } -> List

Map each entry through the closure. The result is a *list* of the closure's results, not a map — matching Groovy.

println([a: 1, b: 2].collect { k, v -> k + v })   // => [a1, b2]

# findAll

map.findAll { k, v -> … } -> Map

Keep the entries the closure accepts, as a new map in source order.

println([a: 1, b: 2].findAll { k, v -> v > 1 })   // => [b:2]

# find

map.find { k, v -> … } -> Map.Entry

The first accepted entry as a `Map.Entry` (which prints as `key=value`), or `null` when none matches.

println([a: 1, b: 2].find { k, v -> v > 1 })   // => b=2

# any

map.any { k, v -> … } -> Boolean

True when the closure accepts at least one entry; stops at the first acceptance.

println([a: 1, b: 2].any { k, v -> v > 1 })   // => true

# every

map.every { k, v -> … } -> Boolean

True when the closure accepts every entry; stops at the first rejection.

println([a: 1, b: 2].every { k, v -> v > 0 })   // => true

# count

map.count { k, v -> … } -> Integer

How many entries the closure accepts.

println([a: 1, b: 2].count { k, v -> v > 1 })   // => 1

# countBy

map.countBy { k, v -> key } -> Map

A map from each closure result to how many entries produced it, keys in first-seen order.

println([a: 1, b: 2].countBy { k, v -> v % 2 })   // => [1:1, 0:1]

# withDefault

map.withDefault { key -> … } -> Map

A copy whose missing-key read runs the closure with that key, *stores* the result under it, and answers it — Groovy's `MapWithDefault`, which grows as it is read.

def m = [:].withDefault { 0 }
println m["z"]
println m   // => 0 then [z:0]

# groupBy

map.groupBy { k, v -> key } -> Map

A map from each closure result to the *sub-map* of entries that produced it, keys in first-seen order.

println([a: 1, b: 2].groupBy { k, v -> v % 2 })   // => [1:[a:1], 0:[b:2]]

# inject

map.inject(seed) { acc, entry -> … } -> Object

Fold over the entries, passing the accumulator and one `Map.Entry`. Unlike the list form the seed is required here.

println([a: 1, b: 2].inject(0) { a, e -> a + e.value })   // => 3

# sort

map.sort([{ entry -> key }]) -> Map

A new map ordered by key, or by the closure's key over each entry. Unlike `List.sort` this never mutates or writes back to the receiver, which is also Groovy's behaviour.

println([b: 1, a: 2].sort())   // => [a:2, b:1]

# max

map.max { entry -> key } -> Map.Entry

The entry whose closure key is greatest, as a `Map.Entry`. The closure is required.

println([a: 2, b: 1].max { it.value })   // => a=2

# min

map.min { entry -> key } -> Map.Entry

The entry whose closure key is least, as a `Map.Entry`.

println([a: 2, b: 1].min { it.value })   // => b=1

GDK — BigDecimal

# toString

decimal.toString() -> String

The decimal rendered with its own scale preserved, so `1.50` keeps both fraction digits and `2.5e7` prints as `2.5E+7`.

println(1.50.toString())   // => 1.50

# abs

decimal.abs() -> BigDecimal

The magnitude, keeping the receiver's scale.

println((-1.5).abs())   // => 1.5

# negate

decimal.negate() -> BigDecimal

The receiver with its sign flipped.

println(1.5.negate())   // => -1.5

# toBigDecimal

decimal.toBigDecimal() -> BigDecimal

The receiver itself — the identity conversion, provided so a script can call it uniformly on a String or a decimal.

println(1.50.toBigDecimal())   // => 1.50

# intValue

decimal.intValue() -> Integer

Truncate toward zero to an integer. This discards the fraction rather than rounding it.

println(1.99.intValue())   // => 1

# longValue

decimal.longValue() -> Long

Truncate toward zero to a 64-bit integer — the same value `intValue` yields, since groovyrs holds one integer width.

println(1.99.longValue())   // => 1

# toInteger

decimal.toInteger() -> Integer

A truncating conversion, identical to `intValue`.

println(2.9.toInteger())   // => 2

# toLong

decimal.toLong() -> Long

A truncating conversion, identical to `longValue`.

println(2.9.toLong())   // => 2

# round

decimal.round() -> Integer

Round to the nearest integer — the one conversion here that does not truncate.

println(1.50.round())   // => 2

# doubleValue

decimal.doubleValue() -> Double

Convert to an IEEE double, giving up the exact decimal scale.

println(1.50.doubleValue())   // => 1.5

# toDouble

decimal.toDouble() -> Double

Identical to `doubleValue`.

println(0.25.toDouble())   // => 0.25

# floatValue

decimal.floatValue() -> Float

Identical to `doubleValue` — groovyrs has one IEEE width, so there is no narrowing to 32-bit precision.

println(1.25.floatValue())   // => 1.25

# toFloat

decimal.toFloat() -> Float

Identical to `floatValue`.

println(1.25.toFloat())   // => 1.25

GDK — java.lang.Class

# getName

clazz.getName() -> String

The fully-qualified class name. A script-declared class answers its bare name, as in Groovy's default package.

println("x".getClass().getName())   // => java.lang.String

# getTypeName

clazz.getTypeName() -> String

The qualified name — the same string `getName` yields.

println([1].getClass().getTypeName())   // => java.util.ArrayList

# getCanonicalName

clazz.getCanonicalName() -> String

The qualified name again. groovyrs models no nested or array classes, the two cases where the JDK's canonical name differs from `getName`.

println(1.getClass().getCanonicalName())   // => java.lang.Integer

# getSimpleName

clazz.getSimpleName() -> String

The last dot-separated segment of the qualified name.

println("x".getClass().getSimpleName())   // => String

GDK — Map.Entry

# getKey

entry.getKey() -> String

The entry's key. Map keys are held as strings, so this always yields a String.

println([a: 1].find { k, v -> true }.getKey())   // => a

# getValue

entry.getValue() -> Object

The entry's value, of whatever type the map holds.

println([a: 1].find { k, v -> true }.getValue())   // => 1

Properties

# size

value.size -> Integer

The count property on a String, list, or map. On a map this rule is *not* reached: a map's property access is only a key read, so `[a: 1].size` is `null` while `[size: 9].size` is `9`.

println([1, 2].size)
println([a: 1].size)   // => 2 then null

# length

value.length -> Integer

A synonym of the `size` property, with the same map exception.

println("abc".length)   // => 3

# class

value.class -> Class

The `getClass()` property, available on every value but a map — where it is a key read like any other name. On a class instance a declared field named `class` still wins.

println("a".class.simpleName)   // => String

# name

clazz.name -> String

The qualified name of a `java.lang.Class` handle, through Groovy's getter-to-property rule.

println(1.5.class.name)   // => java.math.BigDecimal

# typeName

clazz.typeName -> String

The qualified name of a class handle — the property form of `getTypeName()`.

println("x".class.typeName)   // => java.lang.String

# canonicalName

clazz.canonicalName -> String

The qualified name of a class handle — the property form of `getCanonicalName()`.

println([1].class.canonicalName)   // => java.util.ArrayList

# simpleName

clazz.simpleName -> String

The last segment of a class handle's qualified name.

println([1].class.simpleName)   // => ArrayList

# key

entry.key -> String

The key of a `Map.Entry`, the property form of `getKey()`.

println([a: 1].find { k, v -> true }.key)   // => a

# value

entry.value -> Object

The value of a `Map.Entry`, the property form of `getValue()`.

println([a: 1].max { it.value }.value)   // => 1

Class Member Hooks

# toString

def toString() { … }

Declare it and `println`, string concatenation, GString interpolation, and `list.join` all render the instance through it.

class P { def x = 1; String toString() { "P($x)" } }
println(new P())   // => P(1)

# equals

def equals(other) { … }

Decides `==` and `!=` for instances of the class — but only when the class does not also define `compareTo`, which takes precedence.

class P { def equals(o) { true } }
println(new P() == new P())   // => true

# compareTo

def compareTo(other) { … }

Models `Comparable`. It drives `<`, `>`, `<=`, `>=`, `<=>`, `sort`, `unique`, `max`, `min` — and equality too, where `==` becomes `compareTo(…) == 0`.

class P { def n = 1; def compareTo(o) { 0 } }
println(new P() == new P())   // => true

# asBoolean

def asBoolean() { … }

Defines the instance's Groovy truth for `if`, `while`, `&&`, `||`, `?:`, and `findAll`. An instance whose class does not declare it is always truthy.

class P { def asBoolean() { false } }
if (new P()) println("t") else println("f")   // => f

# getAt

def getAt(index) { … }

Defines the `[…]` subscript on the instance. The subscript is reported as a missing `getAt` when the class does not declare one.

class P { def getAt(i) { i * 2 } }
println(new P()[4])   // => 8

# plus

def plus(other) { … }

The `+` operator on the instance. `++` also dispatches here — groovyrs does not model Groovy's `next`.

class P { def plus(o) { 42 } }
println(new P() + 1)   // => 42

# minus

def minus(other) { … }

The `-` operator on the instance, and the operator `--` dispatches to.

class P { def minus(o) { 7 } }
println(new P() - 1)   // => 7

# multiply

def multiply(other) { … }

The `*` operator on the instance.

class P { def multiply(o) { 6 } }
println(new P() * 2)   // => 6

# div

def div(other) { … }

The `/` operator on the instance. It is resolved inside the `GDIV` builtin rather than through the numeric hook the other operators use.

class P { def div(o) { 9 } }
println(new P() / 1)   // => 9

# remainder

def remainder(other) { … }

The `%` operator on the instance. Groovy maps `%` to `remainder`, not to `mod` — verified against Apache Groovy 5.0.7.

class P { def remainder(o) { 3 } }
println(new P() % 2)   // => 3

# negative

def negative() { … }

Unary `-` on the instance.

class P { def negative() { -5 } }
println(-new P())   // => -5

# power

def power(other) { … }

Groovy's `**` operator method. The mapping exists in the runtime, but groovyrs's lexer has no `**` token, so nothing can reach this hook today.

class P { def power(o) { 8 } }
println(new P().power(3))   // => 8

# get<Name>

def getName() { … }

A getter. Reading the property `obj.name` prefers this method over the raw field, and calling `obj.getName()` falls back to the field when no such method exists. Note that a getter which reads its own backing field re-enters itself in the current build, so the body must not name the field it fronts.

class P { def x = 1; def getX() { 99 } }
println(new P().x)   // => 99

# set<Name>

def setName(value) { … }

A setter. Writing `obj.name = v` prefers this method over the raw field write. As with the getter, assigning to the backing field of the same name re-enters the setter in the current build, so write through a differently-named field.

class P { def y = 0; def setX(v) { y = v * 2 } }
def p = new P()
p.x = 5
println(p.y)   // => 10

Throwables

# Throwable

new Throwable([String message]) — java.lang

The root of the modeled hierarchy and the only type that declares the `message` field; every descendant inherits it. `catch (Throwable e)` matches everything a script can throw.

try { throw new Throwable("t") } catch (Throwable e) { println(e) }   // => java.lang.Throwable: t

# Exception

new Exception([String message]) — java.lang

The checked-exception root, directly under `Throwable`. It does not match an `Error`, so `catch (Exception e)` will not catch an `AssertionError`.

try { throw new Exception("x") } catch (Exception e) { println(e.message) }   // => x

# Error

new Error([String message]) — java.lang

The unrecoverable-condition root, the sibling of `Exception` under `Throwable`. `AssertionError` lives beneath it.

try { throw new Error("e") } catch (Throwable t) { println(t) }   // => java.lang.Error: e

# RuntimeException

new RuntimeException([String message]) — java.lang

The unchecked-exception root under `Exception`; most of the throwables the runtime raises itself descend from it.

try { throw new RuntimeException("r") } catch (Exception e) { println(e) }   // => java.lang.RuntimeException: r

# IllegalArgumentException

new IllegalArgumentException([String message]) — java.lang

An argument outside a method's contract. Its one modeled subtype is `NumberFormatException`.

try { throw new IllegalArgumentException("bad") } catch (RuntimeException e) { println(e.message) }   // => bad

# NumberFormatException

new NumberFormatException([String message]) — java.lang

Raised by `String.toInteger`, `toLong`, `toDouble`, `toFloat`, and `toBigDecimal` when the text does not parse. The `toBigDecimal` path can raise the message-less form, which prints with no `: message` suffix.

try { "x".toInteger() } catch (NumberFormatException e) { println(e.message) }   // => For input string: "x"

# IllegalStateException

new IllegalStateException([String message]) — java.lang

An operation attempted in the wrong state. groovyrs never raises it itself; it is here for scripts to throw.

try { throw new IllegalStateException("bad") } catch (Exception e) { println(e) }   // => java.lang.IllegalStateException: bad

# ArithmeticException

new ArithmeticException([String message]) — java.lang

Raised by division and remainder with a zero divisor. The message is `Division by zero` for a decimal divisor, `/ by zero` for an integer one, and `Division undefined` for `0/0`.

try { 1 / 0 } catch (ArithmeticException e) { println(e.message) }   // => Division by zero

# NullPointerException

new NullPointerException([String message]) — java.lang

Raised by a method call, property read, or property write on `null` — except `toString`, `equals`, and `getClass`, which `null` answers.

try { null.foo() } catch (NullPointerException e) { println(e.message) }   // => Cannot invoke method foo() on null object

# IndexOutOfBoundsException

new IndexOutOfBoundsException([String message]) — java.lang

Raised by `List.get` for any index outside the list. The `[i]` subscript does not raise it — a list subscript past the end yields `null`.

try { [1].get(5) } catch (IndexOutOfBoundsException e) { println(e.message) }   // => Index 5 out of bounds for length 1

# ArrayIndexOutOfBoundsException

new ArrayIndexOutOfBoundsException([String message]) — java.lang

Raised by a negative subscript whose magnitude exceeds the receiver's length, on a list or a String.

try { [1][-5] } catch (ArrayIndexOutOfBoundsException e) { println(e.message) }   // => Negative array index [-5] too large for array size 1

# StringIndexOutOfBoundsException

new StringIndexOutOfBoundsException([String message]) — java.lang

Raised by a String subscript past the end, naming the half-open range it tried to read.

try { "ab"[9] } catch (StringIndexOutOfBoundsException e) { println(e.message) }   // => Range [9, 10) out of bounds for length 2

# UnsupportedOperationException

new UnsupportedOperationException([String message]) — java.lang

An operation a type does not support. Registered for scripts to throw; the runtime does not raise it.

try { throw new UnsupportedOperationException("no") } catch (RuntimeException e) { println(e.message) }   // => no

# ClassCastException

new ClassCastException([String message]) — java.lang

A bad cast. groovyrs has no cast operator, so only a script raises this.

try { throw new ClassCastException("c") } catch (Exception e) { println(e) }   // => java.lang.ClassCastException: c

# InterruptedException

new InterruptedException([String message]) — java.lang

Thread interruption, directly under `Exception`. A groovyrs script runs on one thread, so this exists for hierarchy fidelity.

try { throw new InterruptedException("i") } catch (Exception e) { println(e) }   // => java.lang.InterruptedException: i

# CloneNotSupportedException

new CloneNotSupportedException([String message]) — java.lang

Registered so the checked-exception branch under `Exception` matches the JDK's shape; groovyrs models no `clone`.

try { throw new CloneNotSupportedException("c") } catch (Exception e) { println(e) }   // => java.lang.CloneNotSupportedException: c

# AssertionError

new AssertionError([String message]) — java.lang

Raised by the `assert cond : message` form. It sits under `Error`, so `catch (Exception e)` does not catch it.

try { assert false : "nope" } catch (AssertionError e) { println(e.message) }   // => nope. Expression: false

# IOException

new IOException([String message]) — java.io

The I/O root. groovyrs performs no file I/O, so only a script raises it.

try { throw new IOException("x") } catch (Exception e) { println(e) }   // => java.io.IOException: x

# FileNotFoundException

new FileNotFoundException([String message]) — java.io

A missing file, under `IOException`.

try { throw new FileNotFoundException("f") } catch (IOException e) { println(e) }   // => java.io.FileNotFoundException: f

# NoSuchElementException

new NoSuchElementException([String message]) — java.util

An exhausted iterator or an absent element, under `RuntimeException`.

try { throw new NoSuchElementException("n") } catch (RuntimeException e) { println(e) }   // => java.util.NoSuchElementException: n

# ConcurrentModificationException

new ConcurrentModificationException([String message]) — java.util

A collection mutated during iteration. groovyrs iterates over a materialised copy and so never raises it itself.

try { throw new ConcurrentModificationException("c") } catch (RuntimeException e) { println(e) }   // => java.util.ConcurrentModificationException: c

# GroovyRuntimeException

new GroovyRuntimeException([String message]) — groovy.lang

The root of Groovy's own runtime failures, under `RuntimeException`. Both dispatch failures below descend from it.

try { throw new GroovyRuntimeException("g") } catch (RuntimeException e) { println(e) }   // => groovy.lang.GroovyRuntimeException: g

# MissingMethodException

new MissingMethodException([String message]) — groovy.lang

Raised whenever a `recv.method(args)` combination falls outside the modeled GDK, rather than mis-running. The message names the method, the receiver's class, and the argument types.

try { "".bar() } catch (MissingMethodException e) { println(e.message) }

# MissingPropertyException

new MissingPropertyException([String message]) — groovy.lang

Raised by a read of an unmodeled property, and by a write to a field the class chain never declared — groovyrs does not grow an object on assignment.

try { 5.zz } catch (MissingPropertyException e) { println(e.message) }   // => No such property: zz for class: java.lang.Integer

# PowerAssertionError

new PowerAssertionError([String message]) — org.codehaus.groovy.runtime.powerassert

What a bare `assert` raises. It overrides `toString` to print `Assertion failed:` followed by the source text with each recorded sub-expression's value laid out under the column it occupied.

try { assert 1 == 2 } catch (AssertionError e) { println(e.getClass().simpleName) }   // => PowerAssertionError

Type Names

# Object

value instanceof Object

True for every non-null value. A qualified name is matched on its last segment, so `java.lang.Object` behaves identically.

println(1 instanceof Object)   // => true

# GroovyObject

value instanceof GroovyObject

True for every non-null value — groovyrs treats it as a synonym of `Object`. Apache Groovy restricts it to objects that implement the interface, and answers false for a `String`.

println("x" instanceof GroovyObject)   // => true

# String

value instanceof String

True for a String value.

println("x" instanceof String)   // => true

# CharSequence

value instanceof CharSequence

True for a String value — the same test `String` performs.

println("x" instanceof CharSequence)   // => true

# GString

value instanceof GString

True for a String value. An interpolated literal collapses to an ordinary String at runtime, so groovyrs cannot distinguish a GString from a plain one here.

def n = 1
println("$n" instanceof GString)   // => true

# Integer

value instanceof Integer

True for an integer value. groovyrs holds one 64-bit integer type, so this does not range-check to 32 bits.

println(1 instanceof Integer)   // => true

# Long

value instanceof Long

True for an integer value — indistinguishable from `Integer` in groovyrs, where Apache Groovy answers false because an integer literal is an `Integer`.

println(1 instanceof Long)   // => true

# Short

value instanceof Short

True for an integer value, with no width check; Apache Groovy answers false because an integer literal is an `Integer`.

println(1 instanceof Short)   // => true

# Byte

value instanceof Byte

True for an integer value, with no width check; Apache Groovy answers false because an integer literal is an `Integer`.

println(1 instanceof Byte)   // => true

# BigDecimal

value instanceof BigDecimal

True for an IEEE double value. This is coarser than the value model: a decimal literal such as `1.5` lives on the host heap and is *not* matched here, while the `d`-suffixed `1.5d` is — the exact inverse of what Apache Groovy answers for the same two literals.

println(1.5d instanceof BigDecimal)
println(1.5 instanceof BigDecimal)   // => true then false

# Double

value instanceof Double

True for an IEEE double value.

println(1.5d instanceof Double)   // => true

# Float

value instanceof Float

True for an IEEE double value — groovyrs has one IEEE width.

println(1.5d instanceof Float)   // => true

# BigInteger

value instanceof BigInteger

True for an IEEE double value. groovyrs models no arbitrary-precision integer, so this name resolves with the other floating types rather than with `Integer` — Apache Groovy answers false for a double.

println(1.5d instanceof BigInteger)   // => true

# Number

value instanceof Number

True for an integer or an IEEE double value.

println(1 instanceof Number)   // => true

# Boolean

value instanceof Boolean

True for a boolean value.

println(true instanceof Boolean)   // => true

# List

value instanceof List

True for a list value — including a range, since a Groovy `Range` is a `java.util.List`.

println([1] instanceof List)   // => true

# ArrayList

value instanceof ArrayList

True for a list value. `ArrayList` is also the class name a list reports from `getClass()`.

println((0..2) instanceof ArrayList)   // => true

# Collection

value instanceof Collection

True for a list value. groovyrs models no `Set`, so `Collection` and `List` answer identically.

println([1] instanceof Collection)   // => true

# Iterable

value instanceof Iterable

True for a list value. Note that a String and a map are both iterable in a `for (x in …)` loop yet do not answer true here.

println([1] instanceof Iterable)   // => true

# Map

value instanceof Map

True for a map value, whether it is a fusevm hash or an insertion-ordered host map.

println([a: 1] instanceof Map)   // => true

# LinkedHashMap

value instanceof LinkedHashMap

True for a map value — and the accurate one, since every groovyrs map preserves insertion order.

println([a: 1] instanceof LinkedHashMap)   // => true

# HashMap

value instanceof HashMap

True for a map value, even though groovyrs never produces an unordered map.

println([a: 1] instanceof HashMap)   // => true

Inline Rust

# rust

rust { … }

An inline Rust block. The parser rewrites it to a `__rust_compile("<base64>", line)` call, which hands the body to fusevm's FFI at run time: it is compiled to a cdylib once and cached on disk under `FUSEVM_FFI_DIR`, so a second run of the same block is a cache hit.

rust {
    #[no_mangle]
    pub extern "C" fn add(a: i64, b: i64) -> i64 { a + b }
}
println(add(2, 3))   // => 5

# __rust_compile

__rust_compile(base64Body, line)

The desugar target a `rust { … }` block lowers to — the `GFFI_COMPILE` builtin. It is not meant to be written by hand; a script that does so is calling the FFI compiler directly.

// emitted by the parser for every `rust { … }` block

More