Skip to main content

Enactor Script Syntax

Enactor Script supports the full syntax of standard Java, and then relaxes many of Java's rules to make writing small scripts faster and more convenient. This page walks through the standard syntax, the "loose" scripting-friendly variants Enactor Script adds on top of it, and a few convenience features that make everyday scripting easier.

Standard Java Syntax

Because Enactor Script is a Java interpreter, any statement or expression you would normally write inside a Java method - variable declarations, method calls, math expressions, loops, and so on - works exactly as it does in Java.

// Use a hashtable
Hashtable hashtable = new Hashtable();
Date date = new Date();
hashtable.put( "today", date );

// Print the current clock value
print( System.currentTimeMillis() );

// Loop
for (int i=0; i<5; i++)
print(i);

// Pop up a frame with a button in it
JButton button = new JButton( "My Button" );
JFrame frame = new JFrame( "My Frame" );
frame.getContentPane().add( button, "Center" );
frame.pack();
frame.setVisible(true);

Loosely Typed Java Syntax

On top of standard Java syntax, Enactor Script lets you refer to a variable without declaring it first and without specifying a type. Type checking still happens, but it happens at runtime rather than compile time. An untyped variable can hold any value - including Java primitives such as int or boolean - because Enactor Script always tracks the real underlying type and only lets you use the value in ways that type allows.

The same example from above can be written without any type declarations at all:

// Use a hashtable
hashtable = new Hashtable();
date = new Date();
hashtable.put( "today", date );

// Print the current clock value
print( System.currentTimeMillis() );

// Loop
for (i=0; i<5; i++)
print(i);

// Pop up a frame with a button in it
button = new JButton( "My Button" );
frame = new JFrame( "My Frame" );
frame.getContentPane().add( button, "Center" );
frame.pack();
frame.setVisible(true);

Exception Handling

try/catch blocks behave exactly as they do in Java. If you want, you can also loosen the typing on the caught exception by leaving its type out of the catch clause:

try {
int i = 1/0;
} catch ( ArithmeticException e ) {
print( e );
}
try {
...
} catch ( e ) {
print( "caught exception: "+e );
}

Basic Scoping of Variables

Variable scoping follows Java's rules as closely as possible. The complication is that Java has no concept of a "loose" variable - one that can be used without being declared first - so Enactor Script has to define what that means. The rule is simple: an untyped variable, when assigned, defaults to the local scope of wherever the assignment happens.

Blocks

Blocks are the statements between a pair of curly braces {}. Just like in Java, a block defines a level of scope for typed variables - a typed variable declared inside a block is local to that block. Untyped variables are not constrained by blocks; they behave as if they had been declared at the enclosing scope.

// Arbitrary code block
{
y = 2; // Untyped variable assigned
int x = 1; // Typed variable assigned
}
print( y ); // 2
print( x ); // Error! x is undefined.

This applies to any block statement - if, while, try/catch, and so on:

// Same with any block statement: if, while, try/catch, etc.
if ( true ) {
y = 2; // Untyped variable assigned
int x = 1; // Typed variable assigned
}
print( y ); // 2
print( x ); // Error! x is undefined.

The same rule applies to a for loop's initializer: a typed initializer variable stays local to the loop, while an untyped one leaks into the enclosing scope.

for( int i=0; i<10; i++ ) { // typed for-init variable
j=42;
}
print( i ); // Error! 'i' is undefined.
print( j ); // 42
for( z=0; z<10; z++ ) { } // untyped for-init variable
print( z ); // 10

Variable Modifiers

The standard Java variable modifiers - private, protected, public, final, transient, volatile, static - may be applied to typed variables. Of these, only final currently has any effect; the rest are accepted but ignored. Modifiers cannot be applied to untyped variables.

Convenience Syntax

JavaBean Properties

Enactor Script lets you access JavaBean properties as if they were plain fields. A property is really a pair of getXxx()/setXxx() methods, and Enactor Script maps field-style access onto them automatically:

button = new java.awt.Button();
button.label = "my button"; // Equivalent to: button.setLabel("my button");
print( button.label ); // Equivalent to print( button.getLabel() );

Boolean properties may also use the is getter convention:

Float f = new Float(42f);
print( f.infinite ); // Equivalent to print( f.isInfinite() ); // false

If an object has both a real field and a bean property of the same name, the real field wins. To sidestep the ambiguity entirely, Enactor Script also provides a uniform curly-brace syntax that works for JavaBean properties as well as Hashtable and Map entries:

b = new java.awt.Button();
b{"label"} = "my button"; // Equivalent to: b.setLabel("my button");
h = new Hashtable();
h{"foo"} = "bar"; // Equivalent to: h.put("foo", "bar");

Enhanced 'for' Loop

Enactor Script supports the Java 5 style enhanced for loop for iterating over collections and arrays, regardless of typing:

List foo = getSomeList();

for ( untypedElement : foo )
print( untypedElement );

for ( Object typedElement: foo )
print( typedElement );
int [] array = new int [] { 1, 2, 3 };

for( i : array )
print(i);
for( char c : "a string" )
print( c );

Switch Statements

switch in Enactor Script is not limited to numeric types - you can switch on objects such as Date and String, which are compared using their equals() method.

dateobj = new Date();

switch( dateobj )
{
case newYears:
break;
case christmas:
break;
default:
}

Auto Boxing and Unboxing

"Boxing" and "unboxing" describe the automatic wrapping of a primitive value in its wrapper class, and the automatic unwrapping when a wrapper is used where a primitive is expected. Enactor Script has supported this for a long time, well before it became a language feature in standard Java.

int i=5;
Integer iw = new Integer(5);
print( i * iw ); // 25
Vector v = new Vector();
v.put(1);
int x = v.getFirstElement();

Importing Classes and Packages

You can refer to a class by its fully qualified name, or import it (or an entire package) just like in standard Java:

// Standard Java
import javax.xml.parsers.*;
import mypackage.MyClass;

Unlike Java, an import statement in Enactor Script can appear anywhere - even inside a method - not just at the top of a file. If two imports conflict, the later one wins.

Enactor Script also supports a "super import" that maps every class on the classpath in one go. This is mainly useful for interactive/ad-hoc use, since scanning the whole classpath has a real cost:

import *;

Default Imports

A number of common packages are imported by default, so you do not have to import them yourself: javax.swing.event, javax.swing, java.awt.event, java.awt, java.net, java.util, java.io, and java.lang.

Document Friendly Entities

To make it easier to embed Enactor Script inside other document formats such as XML, every common operator has an alternate text form that avoids characters that would otherwise need escaping:

Operator FormSymbol
@gt>
@lt<
@lteq<=
@gteq>=
@or||
@and&&
@bitwise_and&
@bitwise_or|
@left_shift<<
@right_shift>>
@right_unsigned_shift>>>
@and_assign&=
@or_assign|=
@left_shift_assign<<=
@right_shift_assign>>=
@right_unsigned_shift_assign>>>=