Ejscript Statements

Ejscript implements the full set of statements defined by the ECMAScript specification. Ejscript statements are similar to their C/C++ counterparts but with some object oriented additions. Statements represent the body of an Ejscript program by combining logic with expressions.

Statement Overview

The following table describes the major statements supported by Ejscript. See after the table a more detailed description of each statement.

Statement Syntax Brief Description
; ; Empty statement. Do nothing.
break break label Jump to the designated label. (Not supported)
case case expression: Define a case block inside a switch statement.
cast variable cast Type Cast the variable to the specified type.
continue continue Continue the loop at the top of the loop.
catch catch (error) { body } Catch an exception.
class class className { body } Define a new class.
default default: Begins the default case for a switch statement.
delete delete property; Delete the named property (or variable).
do do { body } while Do / while statement
finally finally { body } Define a try / finally code block that is always executed.
for for (init; condition; increment)
statement
Standard for loop.
for (.. in for (variable in object)
statement
Iterate over all property names in an object.
for each for each (variable in object)
statement
Iterate over all properties in an object.
function function [get | set] name([arg1 [... , arg2])
{ statements }
Define a function.
if / else if (expression)
statement
Conditionally execute a statement.
interface interface Name { body } Define a type interface.
let let identifier [ = value ] [... , identifier [ = value]]; Declare and initialize block scope variables.
module module name { block } Group a block of code as a module.
new new ClassName(args) Create a new object instance based upon a class.
return return [expression]; Return a value from a function.
super super(args) Invoke the super constructor. Also used to refer to the super type.
switch switch (expression) { body } Multi-case conditional switch.
throw throw new ExceptionClass() Throw an exception.
try try { body } Execute code within an exception try / catch handler.
use use pragmas Define program pragmas.
var var identifier [ = value ] [... , identifier [ = value]]; Declare and initialize variables.

class

Ejscript provides a familiar class paradigm as a convenient wrapper over classical prototype-based inheritance. Ejscript classes support single inheritance and are similar to the classes provided in ActionScript and other languages such as Java.

class Shape {
    var x, y
    var color
}
class Circle extends Shape {
    var radius
}
circle = new Circle

Unlike Java or .NET, classes are run-time objects themselves and are accessible as global objects. Static methods are implemented as functions contained in the run-time class objects.

Scoping Visibility

Inside classes, properties and methods can be annotated with the visibility qualifiers:

The default visibility qualifier is "internal". These visibility qualifiers are implemented using the namespace facility and so the "use default namespace public" statement will change the default qualifier for all subsequent declarations.

class Shape {
    protected var x, y              /* Visible to any subclasses */
    private var color               /* Private to this class */
    use default namespace public    /* Default declarations public after here */
    function show() {
    }
    private inner() { }             /* Private function */
}

cast

To force a type conversion from one type to another, use the cast operator.

n = "1234" cast Number
Reflect(n).typeName

This will cast the string "1234" to a number and assign the result to "n". It then uses the Reflection API to print the type of n.

for

The for statement provides the basic looping construct for Ejscript. Similar to the C for loop it implements an initialization, conditional and increment phases of the statement. The for statement also provides a for / in construct for iterating or enumerating elements of an object.

for (initialization; conditional; increment)
statement

For example:

for (let i = 0; i < 10; i++) {
print("i is " + i)
}

You can put any expression or statement in the initialization or increment sections. In this example, we simply define a variable and initialize it to zero in the initialization section and increment it each time after the statement body is executed. The conditional expression will evaluate to a boolean value which if true, allows the statement body to be executed. The increment expression is evaluated after the statement body is executed and before the conditional is re-tested.

for .. in

The for in statement is a powerful variant of the for statement that allows you to iterate over all the property names in an object. It uses the following syntax:

for (variable in object)
statement

This statement will execute the statement body for each property name in the object. Each time, the variable will be set to the name of the next property in the object. The order of the walk through all the properties is determined by the order of property creation. NOTE: it will not be set to the property value, but will be set to the property name. For example:

for (let v in customer) {
print("customer." + v + " = " + customer[v])
}

This will print "customer.propertyName = value" for each property defined in the customer object.

for each .. in

The for each statement allows you to enumerate the property values in an object. It uses the following syntax:

for each (variable in object)
statement

This statement will execute the statement body for each property value in the object. Each time, the variable will be set to the next property value in the object. The order of the walk through all the properties is according to the order of property creation.

for each (var v in customer) {
print(v)
}

function

The function statement defines a new global function according to the syntax:

function name([arg1 [... , arg2]) {
statements
}

The function name must be an identifier and not a reserved word and is followed by an optional list of arguments. Between braces, a set of statements define the function body. For example:

function min(arg1, arg2) {
if (arg1 < arg2) {
return arg1
} else {
return arg2
}
}

Function declarations can also be nested, i.e. a function may be defined in the statement body within an outer function. In this manner, the inner function will only be visible within the scope of the outer function.

When the function is invoked, a new local variable store is created so that any variables declared and used in the function will be private to the function. Functions invoke other functions and each function will have its own local variables. If a variable is assigned to without using the var statement, the variable will be created in the global variable store.

When functions are defined within a class, they become methods of that class and are only accessible via instances of the class. If the function is declared to be static, it requires a class object instance.

if / else

The if statement is the primary conditional execution ability with Ejscript. It takes the form:

if (expression) 
statement
[ else statement ]

The expression is evaluated and if true, the first statement is executed. If an else phrase is added and the expression evaluates to false, then the else statement will be executed.

Statements may be grouped using braces. For example:

if (i < j) {
print("i is " + i)
print("j is " + j)
} else {
// Do something
}

The conditional expression may be a compound conditional expression. For example:

i = 0
j = 1
if (i < j || j != 0 || getToday() == "sunday") {
// Do something
}

Ejscript uses lazy evaluation where if the first expression "i < j" is true, then the following expressions will not be evaluated. In the example above, getToday() will not be called as "i" is less than "j".

interface

An interface definition creates a type object that may be used to specify the required methods and properties for an implementing class. Unlike Java interface, Ejscript interfaces may contain functions with actual code. A class then has a choice to either implement an interface by using the implements keyword, or it can extend the interface by using the extends keyword.

interface Shape {
    function move()
}
class Chape implements Shape {
    function move() {
    }
}

module

JavaScript is sometimes beset by scope and name-conflict issues. Particularly when using mash-ups or when "programming in-the-large", the lack of a comprehensive name resolution facility is a great drawback.

Ejscript rectifies this problem by providing a powerful module facility. This consists of module and require directives, and a module packaging mechanism and loader.

Grouping related declarations inside a module block instructs the compiler to create a module and to qualify the contained declarations with the module name as a namespace.

module MyStuff {
    var x = "mine"
}
module YourStuff {
    var x = "yours"
}

This code creates two modules and two variables both called x. However, it qualifies each "x" with the name of the module. If your code utilizes the require directive, you can specify which module you want to use and which variable "x" you need.

home> ejs
require MyStuff
print(x)
mine

Multiple require directives can be employed and they stack and nest as expected. A require directive only applies to the block in which it is defined and from the point of declaration onward.

require

To load and access a module, you need to add a require pragma to you code.

require moduleName

This will add the module named "moduleName" to your program. This adds the module to the set of open namespaces in the current file scope.

return

The return statement is used to supply a return value inside a function. The return statement may appear without an expression to cause the function's execution to terminate and return to the caller.

A return expression may be a simple value or it may be an object reference. For example:

function myFunc(x) {
if (x == 0) {
return null
}
return new MyObj(x)
}

Switch, Case, Default

The switch statement provides a multi-case conditional statement. It can switch on numeric or string valued types. Like switch in all classical C languages, control flow falls through each case statement to the one below. Thus you must use a break statement if you want each case block to be discrete.

switch (x) {
case "Today"
    /* Do Something */
    break
case "Tomorrow"
    /* Do Something else */
    break
default:
    break
}

Try, Catch, Finally

The try statement encapsulates a block of code and captures any exceptions for handling by relevant catch or finally blocks. If an unhandled exception occurs while executing the try block code, a non-local goto will occur and control will resume at the first qualifying catch if a catch block is defined.

Regardless of whether an exception occurs or not, any code in a finally block will be executed after the try block. Finally blocks are a convenient way of freeing resources and doing cleanup regardless of whether an exception occurs or not.

try {
    j = 0
    x = 100 / j
    /* Won't get here */
}
catch (error) {
    /* Handle error here. The error variable has the exception object */
}
finally {
}

Catch blocks can omit their argument variable or they can annotate it with a type qualifier. If the type is qualified, then multiple catch blocks, each with a different type specifier, can be used. This is an Ejscript enhancement over standard ECMAScript.

try {
    doSomething()
    throw new MyError("My custom Error")
    /* Won't get here */
}
catch (error: MyError) {
    /* Only get here for my custom errors */
}
catch (error){
    /* Handle error here */
}

Try blocks can be nested arbitrarily.

use

The use statement applies various modifiers to the code environment. These are typically called pragma directives.

use namespace

Use Default Namespace

To set the default namespace for all following directives, add the use default namespace pragma.

use default namespace NAME

This will add the namespace to the current block scope as the top most (first) namespace to search. NAME can be either a literal namespace in quotes or a namespace variable.

Use Strict

To set the compiler to employ more strict type checking and validation, add the use strict pragma.

use strict

This will put the current source file into strict type checking mode. In strict mode all variables must be declared and typed.

Use Standard

To set the compiler into standard relaxed type checking, use the use standard pragma.

use standard

This will put the current source file into standard type checking mode. In this mode, variables it is optional if variables are pre-declared and/or type annotated.

var and let

The var statement declares variables and initializes their values. Although not strictly required by the language to pre-declare variables, it is good practice to do so.

The var statement defines variables at the top level of the current function, class or file. The let statement defines variables inside the current block only. It is useful to use let declarations for temporary variables to avoid name clashes with other declarations of the same name.

The var statement takes the form:

var identifier [ = value ] [... , identifier [ = value]];
let identifier [ = value ] [... , identifier [ = value]];

For example:

var x = 2
var y = 4
var a, b = 2, c = "sunny day"; { /* This defines another local "x" just for this block */ let x = 7 } print(x) /* This prints the original x == 2 */

If an initializer value is not defined, the identifier will be set to the undefined value.

If the var statement is used within a function, the variable is defined in the local variable store. If it is used outside a function, the variable is defined on the global store. If a variable is assigned without a var statement, then the variable is created on the global store. For example:

x = 2

function myFunc() {
x = 7
}

println("x is " + x);

This code snippet will print "x is 7".

© Embedthis Software. All rights reserved.