Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Overview

ShockScript is a strongly typed, multi-paradigm scripting language.

Note: The document is a work-in-progress, and may lack content.

E4X

ShockScript is mixed with the eXtensible Markup Language (XML), based on the legacy ECMAScript for XML (E4X) extension. It is, in addition, suitable for implementing reactive UI components.

For the Whack Engine, E4X literals create WhackDS nodes by default.

Events

ShockScript incorporates a basic event model and allows easily declaring event types and documenting them. Implementations may extend the event model for working with a Document Object Model.

ESDoc comments

ShockScript supports documentation comments allowing for Markdown notation, special tags such as @throws and media inclusion.

Abbreviation

ShockScript is often abbreviated as ES or ES4 since it looks like the non-existing ECMAScript 4th edition. The conventional ShockScript file extension is .es.

Embed

The Embed() expression may be used for embedding files and media into the program. Its default behavior is to return a data: (for small files) or external URL (for large files).

Embed("thumb.webp")

Note: When returning an external URL, implementations such as Whack Engine use the app:// scheme to fetch a file in the application’s installation directory.

Note: Implementations may support an artifact path interpolation, as in:

Embed("{target}/auto.generated")

That is useful when a build script is involved that generates files at the artifact path.

Including statically

The user may embed files statically as either an UTF-8 encoded string, or ByteArray, rather than obtaining an URL.

Embed("data.txt", "text/plain")
Embed("data.bin", "application/octet-stream")

Language comparison

This section compares ShockScript to other technologies and languages.

React.js

ShockScript incorporates the XML language, and XML literals allow for implementations to produce anything desired, similiar to JSX. There are certain differences to JSX or React.js.

The following demonstrates a basic XML literal for WhackDS:

<w:HGroup>
    <w:Button click&={trace("clicked!")}>button 1</w:Button>
</w:HGroup>

Event handlers

In ShockScript, event handlers are expressed as e&={statementList} (note the ampersand &) as a shorthand to e={function(event){statementList}}. Furthermore, event handlers are conventionally expressed without an on prefix (for instance, click instead of onClick), and they are documented with the @event tag.

Prefixes

ShockScript allows for <q:N>, whose name resolution equals q::N. Dots may be used for entities other than namespaces, as in:

<com.example.components.AppBar/>

For brevity, you do either:

import bc = com.example.components.*;

<bc:AppBar/>

or:

import com.example.components.*

<AppBar/>

Interpolation

Interpolation works similiarly to React.js, except for HTML.

<w:VGroup>
    {undefined}
    {null}
    {node}
    {nodeList}
    {plainText}
    {number}
</w:VGroup>

Interpolating attributes uses { object } and not { ...object } and must appear at most once at the end of all attributes:

<w:Button {arguments}>click me</w:Button>

States

Unlike React.js, in WhackDS there is no risk of accessing an outdated state’s value, due to how states are constructed.

package = com.example.components;

public function HelloWorld() : whack.ds.Node {
    // x
    [State]
    var x:uint = 0;

    // layout
    return (
        <w:VGroup>
            <w:Label>clicked {x} times</w:Label>
            <w:Button click&={x++}>click me</w:Button>
        </w:VGroup>
    );
}

The state’s initializer represents the state’s initial value.

Like React.js, there is no transitive detection of mutation methods; therefore, the following is necessary over an array .push(v):

x = [...x, v];

As to Map objects:

m = { ...m, k: v };

Bindables

In WhackDS the concept of “refs” is called bindables.

package = com.example.components;

//
public function HelloWorld() : whack.ds.Node {
    [Bindable]
    var button:Button?;

    //
    whack.ds.useEffect(function() {
        trace(button!);
    }, []);

    return (
        <w:Button bind={button}>click me</w:Button>
    );
}

Contexts

Context usage is represented as whack.ds.ContextValue.<T> objects, although they are used as natural Context-annotated locals.

function Example() : whack.ds.Node {
    //
    [Context("ExampleContext")]
    const example;

    return (
        <></>
    );
}

Effects

The popular “useEffect” hook requires the second argument, preventing mistakes. For listening to any changes, use "*".

whack.ds.useEffect(function() {
    //
    return function() {
        // cleanup
    };
}, [dep1, ...depN]);

whack.ds.useEffect(function() {
    //
}, "*");

Styling

Unlike with React.js, there is built-in support for linking style sheets in a WhackDS component.

<w:Group>
    <w:Style><![CDATA[
        :host {
            background: red;
        }
    ]]></w:Style>
</w:Group>

More on style sheets

MXML

The MXML language, as part of the Apache Flex framework, was used for describing UI components in an intuitive way. ShockScript uses XML literals semantically similiar to the React.js + JSX technologies, but designed to feel close to MXML in readability.

The following demonstrates a basic UI component implemented in WhackDS:

package = com.example.components;

public function AppBar() : whack.ds.Node {
    return (
        <w:HGroup>
            <w:Button click&={trace("clicked!")}>button 1</w:Button>
        </w:HGroup>
    );
}

Event handlers

In MXML, event handlers were expressed as e="statements". In ShockScript, they are expressed as e&={statements} (note the ampersand &) as a shorthand to e={function(event){statements}}.

Note: Although not demanded as such, as opposed to React.js + DOM, event handlers are conventionally expressed without a on prefix, such as click&={trace("clicked!")} rather than React.js onClick={e=>{console.log("clicked!")}}. Event parameters are conventionally given the @event tag in the ESDoc comments. Classes continue using the Event meta-data, though without needing the @eventType tag.

Rendering components

The Whack Engine’s WhackDS feature allows programmers to implement UI components as functions that wrap around the built-in class-based components of Whack Engine. The component is rendered by evaluating the function initially and whenever a state changes.

Effects

The whack.ds.useEffect hook may be used to detect state, parameter or derived changes as well as the component mount and unmount phases.

whack.ds.useEffect(function() {
    // cleanup
    return function() {
        //
    };
}, [dep1, ...depN]);

whack.ds.useEffect(function() {
    //
}, "*");

When the dependencies list is empty ([]), the hook is equivalent to a component mount/unmount event, with the unmount phase handled through the returned function.

whack.ds.useEffect(function() {
    // did mount
    return function() {
        // unmount
    };
}, []);

States

In the top-level of a component, declare states using the State meta-data:

[State]
var counter:uint = 0;

The initial value of counter is zero, although that initializer evaluates only the first time the component renders.

Overwriting a state with a different value (as by an equal() comparison) will re-render the component.

Note that, like with React.js, arrays and structures as states will not trigger a re-render on operations like .push(); instead the programmer needs to reconstruct the array or structure, like in:

list = [...list, v];

Bindables

In the top-level of a WhackDS component, declare bindables by using the Bindable meta-data. Bindables have certain use-cases, such as persisting a value across renders, and extracting class-based components from certain tags (in which case the bind attribute is used).

[Bindable]
var button:Button? = null;

return (
    <w:Button bind={button}>click me</w:Button>
);

Contexts

In the top-level of a component, reflect inherited contexts by using the Context meta-data.

[Context("ThemeContext")]
const theme;

Capture safety

Unlike in React.js combined with TypeScript, states, bindables (“refs”) and context values are captured by reference from nested functions, guaranting the “outdated” value of, say, a state, is never captured, which facilitates development by requiring no additional bindable declaration.

Styling

Unlike with React.js, there is built-in support for linking style sheets in a WhackDS component.

<w:Group>
    <w:Style><![CDATA[
        :host {
            background: red;
        }
    ]]></w:Style>
</w:Group>

More on style sheets

ActionScript 3

ShockScript looks like ActionScript 3. This section describes several details that changed on the language.

Primitive types

  • ShockScript does have more numeric types close to ECMAScript 4.
  • Primitive types are in a lowercase form, adapting more from the ECMAScript 4 proposal, but without backwards compatibility.

string type

The string type stores an UTF-8 encoded text, not an UTF-16 encoded text.

"\u{10ffff}".length    // utf-8 length
"\u{10ffff}".charAt(0) // code point at byte 0

for each (var ch in "shockscript".chars()) {
    // ch:uint
}

"shockscript".chars().length() // code point length

Short package

The package definition required a block in ActionScript 3. ShockScript introduces a top pragma for declaring the whole program as a package.

package = org.generalrelativity.foam.equations;

public class RungeKutta {
    //
}

Include directive

The include directive is not included in ShockScript.

Dynamic

The Object type is not dynamic per se, nor does it contain undefined, nor are there dynamic classes, nor are there legacy ECMAScript prototype objects. Only the * type is dynamic and contains undefined.

Matching: The str.match resulting object is slightly different, but still supports indexing.

Obtaining constructor: o.meta::class()

Nullability

Types except * are non-nullable by default. Use T? or ?T as a shorthand to (null, T), and T! as a way to exclude undefined and/or null.

Overriding methods

  • Instance methods may override another method and include additional optional parameters (including the rest parameter).
  • Instance methods may override another method and return a more contravariant result type.
class A {
    function m() {}
}
class B extends A {
    override function m(...rest:[float]) {}
}

“in” operator

The in operator behaves differently. It triggers meta::has() which is in general used for determining whether a collection contains a specific value; for Maps it determines whether a pair key exists; for XML and XMLList objects it performs the same E4X behavior.

trace(e in arr);
trace(k in m);

Filter operator

The filter operator has been modified to use a * identifier rather than cluttering the lexical scope with dynamic names.

xnode.(*.@x.startsWith("abc"))

With statement

The with statement is modified to use the * identifier to avoid cluttering the lexical scope.

with (o) {
    *.x =
    *.y = 10;
}

“this” capture

The this object is always captured from the parent activation in nested activations; there is no way to override the this object with another value.

class A {
    function m() {
        function n() {
            // this:A
        }
    }
}

E4X

XML literals do not produce XML or XMLList unless the inference type is one of these; they are implementation-defined by default. Such expressions have also undergone incremental syntax additions:

  • <t a/> equals <t a={true}/>
  • <t e&={}/> equals <t e={function(event){}}/> or <t e={function(){}}/>
<w:VGroup>
    <w:Label variant="heading">Welcome</w:Label>
    <w:Button click&={trace("clicked me");}>Click me</w:Button>
</w:VGroup>

Note: XML(<tag/>) equals var _loc_0:XML = <tag/>;.

Events

Events are declared without defining related static constants, as ShockScript performs vast type inference; thus, the ASDoc @eventType tag does not exist in ShockScript.

/** Some event */
[Event(name="act", type="Event")]
/** Some class */
class A extends EventTarget {}

Note: The @event tag introduced in ShockScript is used for documenting events better in reactive systems that use record types rather than classes for component parameters.

Embedding

Embedding files is much easier in ShockScript. The following returns typically an app:// URI for a file that will be automatically added to the application’s installation directory.

trace(Embed("flower.webp"));

Note: Implementations may support interpolating an artifact directory at the Embed path, such as {target}.

trace(Embed("{target}/auto.generated.bin"));

This is useful for when a build script generates a file at an artifact directory.

For static embedding, use a second option as either "text/plain" or "application/octet-stream".

Embed("data.txt", "text/plain")               // :string
Embed("data.bin", "application/octet-stream") // :ByteArray

Variable shadowing

In ShockScript the following is valid in an activation:

var m:* = complexCentral.manager;
// more code...
var m = Manager(m);

Switch fallthroughs

The switch statement does not support fallthroughs, which helps preventing logical bugs by not requiring the break statement.

switch (v) {
    case 0:
        trace("zero");
    case 1:
        trace("one");
    default:
        trace("rest");
}

Switch type

The switch type statement allows for simple type matching:

switch type (v) {
    case (d : Date) {
        //
    }
    default {
        //
    }
}

JavaScript

ShockScript gets too many roots from JavaScript, although more particularly linked to a previously abandoned version, JavaScript 2 (or ECMAScript 4th).

Map data type

The ShockScript’s Map data type differs fundamentally from JavaScript’s Map in that key-value pairs are accessed more naturally. ShockScript resolves the ambiguity between pairs and the prototype by differentiating property read and call.

m.x = 10
m.length()

Note: For a customized type, it might be necessary to access a fixed variable rather than an arbitrary key-value pair; for that, the user may use a fixed expression as in <?fixed={object.q::n}?>.

Variable shadowing

In ShockScript the following is valid in an activation:

var m:* = complex.manager;
// more code...
var m = Manager(m);

“not” keyword

not may be used to negate in or is operators:

e not in a
v is not T

“this” binding

The this binding is fixed and present only in instance methods.

  • Methods like [object Function].apply() do not take a this binding: only the parameters.
  • Instance methods are bound.

Java

Package flexibility

While importing definitions, the user can alias a definition, or even a package.

// ===== Chart.es =====

package = com.example.product.core;

public class Chart {
    //
}

// ===== ChartType.es =====

package = com.example.product.core;

public enum ChartType {
    const BAR;
    const FLOW;
}

// ===== usage =====

import p = com.example.product.core.*;
//
const chartType : p::ChartType = "flow";
//
const chart = new p::Chart(chartType);

E4X

E4X (ECMAScript for XML) comprises the ShockScript language features specifically designed for eXtensible Markup Language.

XML literals

XML literals by default are used for creating implementation-defined objects; however, when the inference type is XML or XMLList, XML literals evaluate to one of these types.

// AppBar.es
package = com.example.components;

public function AppBar() : whack.ds.Node {
    return (
        <w:HGroup>
            <w:Button click&={trace("clicked!")}>button 1</w:Button>
        </w:HGroup>
    );
}

// data.es
package = com.example.datagenerator;

public function data(a:string) : XML {
    return (
        <tag>{a}</tag>
    );
}

{
    const xn = XML(<tag/>);
}

Attributes

<t a/> is equivalent to <t a={true}/>. Accessing XML attributes can be directly done by the @ operator, as in xnode.@x.

Event handlers

Inline event handlers may be expressed as eventName&={statementList} as a shortcut to eventName={function(event){statementList}}, as in:

<w:Button click&={trace("clicked!")}>Click me</w:Button>

If the event has no parameters, then the attribute above is equivalent to eventName={function(){statementList}}.

Interpolation

<w:VGroup {params}>
    {undefined}
    {null}
    {node}
    {nodeList}
    {plainText}
    {number}
</w:VGroup>

Filtering

XML and XMLList implement the filter operator.

people.(*.@name == "John")

Descendants

XML and XMLList implement the descendants operator.

xnode..tag

E4X application to Whack

This section describes E4X features specifically when applied as WhackDS nodes.

Native tags

Native tags belong to the implicit w namespace, such as <w:Button>.

The w prefix may be shadowed by user definitions; in that case, to use native tags the user needs to define a namespace like follows:

namespace whack = "http://www.sweaxizone.com/2015/whack";

DOM “data” attributes

data attributes (like data-x) set over native tags, such as <w:Button>, contribute plain data attributes to the underlying DOM element.

Using a WhackDS bindable, the attribute would be accessed as bindable!.@["data-name"].

“key” attribute

The key attribute is reserved for uniquely identifying interpolated collection items.

Linking cascading style sheets

<w:Style> tags are used for linking style sheets to the parent tag and passing arguments to the style sheet (which are referred by the style sheet as param(color)).

package = com.fun.components;

public function Custom() : whack.ds.Node {
    return (
        <w:Button>
            <w:Style color="yellow"><![CDATA[
                :host { color: param(color) }
            ]]></w:Style>
            click me
        </w:Button>
    );
}

If the style sheet is too large, it may be moved out of the ShockScript file; for instance:

// ===== Custom.es =====

package = com.fun.components;

public function Custom() : whack.ds.Node {
    return (
        <w:Button>
            <w:Style source="Custom.css"
                     color="yellow" />
            click me
        </w:Button>
    );
}

// ===== Custom.css =====

:host {
    color: param(color);
}

Style blocks can be conditional, as in:

<w:Style if={condition}>
    ...
</w:Style>

An arbitrary map of parameters (Map.<string, string>) may be passed as well:

<w:Style {map}>
    ...
</w:Style>

Linking style sheets in custom components

For a component to support <w:Style> tags, it simply needs to support a stylesheet : [whack.ds.StyleSheet] parameter.

package = com.fun.components;

public function Custom(vars: {
    stylesheet? : [whack.ds.StyleSheet],
}) : whack.ds.Node {
    //

    return (
        <w:Button>
            <w:Style extend={vars.stylesheet}/>
            click me
        </w:Button>
    );
}

The extend attribute may be used to include externally loaded styles as well.

Specifying inline styles

Use s:n={v} attributes as a shortcut to style={{ ..., n: v }}.

<w:Button s:background="orange">button1</w:Button>

WhackDS

WhackDS is a feature of the Whack engine used for extending the closed set of Whack’s UI component classes with reactive components. It is functionally similiar to React.js, but its syntax is more similiar to Adobe MXML.

Memoization

WhackDS automatically memoizes components, allowing for user customizable parameter/state equality comparison through overriding the [object Object].equals(obj) method. Memoization basically serves the purpose of avoiding re-rendering a component when its parameters do not change.

Just like with React.js, memoizing components has drawbacks such as possibly volatile code regions (such as when internationalizing a product with locale-specific translation strings). In such cases, relying on a WhackDS context will re-render the component when the context changes regardless of whether parameters did or not change.

WhackDS skips re-rendering component if the parent re-renders and the parameters are equals to the previous render; the WhackDS component’s own states updating with a different value will always re-render it.

Note: To avoid old state comparison issues, the properties object must not be reused for mutation (since WhackDS does not know how to clone them).

Style blocks

WhackDS supports style sheets out of the box. Here is a simple example:

<w:VGroup>
    <w:Style><![CDATA[
        :host {
            background: red;
        }
    ]]></w:Style>
</w:VGroup>

How a component is defined

There are different ways to declare a component. Here are some examples:

// ===== Main.es =====

package = com.pso2;

public function Main():whack.ds.Node {
    //
}

// ===== Button.es =====

package = spark.components;

public class Button {
    meta static function call(props:Props):whack.ds.Node {
        //
    }

    public type Props = {
        //
    };
}

// ===== Icon.es =====

package = spark.components;

public enum Icon {
    const CALC;
    const CAMERA;

    public static function component(props:Props):whack.ds.Node {
        //
    }

    public type Props = {
        //
    };
}

Tooling

Building and structuring projects should feel way like Cargo from the Rust language, at least in the Whack engine.

Configuration ease

Configuring ShockScript projects is way easier compared to other technologies.

Unlike NPM + TypeScript, you do not have to worry about transpilation or whatsoever when building libraries or applications; not even comparable as ShockScript targets WebAssembly. If you were implementing a library in NPM + TypeScript, you were forced to transpile TypeScript to JavaScript first due to the tsconfig.json file which is ignored from third-party dependencies while compiling, otherwise you would get inconsistent transpilation or compiler errors.

Whack case

API documentation

API documentation is automatically built for packages that are published to the Whack package registry.

Namespaces

ShockScript defines properties whose name is tied to a namespace, which is useful for version control and protection.

// ===== FunInternal.es =====

package = com.fun.runner;

/**
 * @private
 */
public namespace FunInternal = "http://www.fun.com/2007/runner/internals";

// ===== Helper.es =====

package = com.fun.runner;

public class Helper {
    /**
     * @private
     */
    FunInternal const cache : [double] = [];

    //
    public function foo() {
        FunInternal::cache.push(0);
    }
}

// ===== friend.es =====

package = com.fun.runner.advanced;

import com.fun.runner.*;

public function friend(helper:Helper) {
    helper.FunInternal::cache.push(10);
}

Namespaces additionally apply to record types.

// ===== Flexible.es =====

package = com.example.product;

/**
 * Flexible version control namespace.
 */
public namespace Flexible = "http://business.com/product/flexible";

// ===== Judgement.es =====

package = com.example.product;

/**
 * Judgement version control namespace.
 */
public namespace Judgement = "http://business.com/product/judgement";

// ===== Pair.es =====

package = com.example.product;

/**
 * Pair.
 */
public type Pair = {
    Flexible::strength : [decimal],
    Judgement::strength : [decimal],
};

Event model

The native EventTarget class is designated for dispatching and listening to events, and actual implementations may use it for implementing a hierarchic DOM event model.

In addition, the IEventTarget interface may be implemented instead of extending the EventTarget class.

Implementing an event target

The following program demonstrates implementing a basic EventTarget subclass that is able to emit events:

/**
 * Some event.
 */
[Event(name="act", type="Event")]
/**
 * Some class.
 */
class Actor extends EventTarget {
    /**
     * Some method.
     */
    function m() {
        this.emit(new Event("act"));
    }
}

Listening to an event

Subscribing to an event looks as follows:

actor.on("act", function() { trace("acting") });

Implementing an event class

Event constructors must always take the event type as the first argument; any other arguments may follow. In the following code the user inherits the Event constructor.

class SomeEvent extends Event {}

EventTarget implementation

It is a rare case for the user to need to implement their own EventTarget class: it may only arise if the user needs EventTarget to work with their own Document Object Model.

emit()

The emit() method is defined as follows:

[Limit("E extends Event*(this)")]
/**
 * Dispatches an event.
 */
function emit.<E>(e:E):boolean {
    // code
}

When the emit() method is used, it will force a new E(...) expression to be a correct Event object construction, by ensuring the first argument identifies a determined event type according to E.

on()

The on() method is roughly defined as follows:

[Limit("E extends Event(this)")]
/**
 * Registers an event listener.
 */
function on.<E>(type:E.name, listener:function(E.type):void) : void {
    //
}

The third parameter was omitted for clarity.

Conditional compilation

NAMESPACE::CONSTANT

NAMESPACE::CONSTANT {
    //
}

NAMESPACE::CONSTANT var x

Iteration

ShockScript features full object-oriented iteration.

iterator.length()
iterator.some(function(v) v > 0)
iterator.(* > 0) // filter

The user may override the key and value iterators by implementing the Iterable.<K, V> interface.

class A implements Iterable.<string, double> {
    /**
     * Iterate keys.
     */
    function keys():Iterator.<string> {
        for (var i = 0; i < 10; i++) {
            yield i.toString();
        }
    }

    /**
     * Iterate values.
     */
    function values():Iterator.<double> {
        for (var i = 0; i < 10; i++) {
            yield i;
        }
    }
}

Environment variables

Environment variables may be read from the project’s .env file using the env::VAR_NAME expression:

env::SECRET

Implementations may include predefined variables when using this expression.

Type matching

“is” operator

v is T

“switch type” statement

switch type (v) {
    case (regex : RegExp) {
        trace("a regex");
    }
    case ([x, y] : [double, double]) {
        trace("a tuple of double");
    }
    default {
        trace("any other");
    }
}

Namespace methods

A class that implements the static meta::call hook may act as a namespace method. This is useful in several cases, including:

  • Assertion methods in the top-level package (for example, assert.equal)
  • Design language components (for instance, one source file declares the whole component, rather than one source file for the rendering method and another source file for the properties record)
package = spark.components;

public class Button {
    meta static function call(props:Props):whack.ds.Node {
        //
    }

    public type Props = {
        //
    };
}

Scope

This not so formal document specifies the syntax, semantics, execution and global objects of the ShockScript language.

Definitions

Code Point

A Code Point as specified by the Unicode standard.

Scalar Value

A Scalar Value as specified by the Unicode standard.

Required function

A function that contains at least one required parameter.

Required method

A method that contains at least one required parameter.

Required constructor

A constructor that contains at least one required parameter.

Notational conventions

Syntactic and lexical grammars

This document uses the following notation to define one or more productions of a nonterminal of the syntactic grammar:

    Symbol :
      Production1 Symbol1
      ProductionN

This document uses double colon (::) notation to define productions of a nonterminal of the lexical grammar:

    Symbol ::
      terminal

The opt subscript is used to indicate that a nonterminal symbol is optional.

    Symbol ::
      Symbol1opt

A bracketed clause or predicate may appear between the rules of a production, such as in:

    Symbol ::
      [lookahead ∈ { 0 }] Symbol1
      [lookahead ∉ { default }] Symbol2
      [lookahead ≠ xml ] Symbol3
      SourceCharacters [but no embedded <![CDATA[]

The «empty» clause is matched by the grammar where other rules do not match otherwise.

    Symbol :
      «empty»

Braces subscripts are used to quantify a rule:

  • Symbol{4} — Four of Symbol
  • Symbol{2,} — At least two of Symbol
  • Symbol{1,4} — One to four of Symbol

Types

This section describes the data types and certain type expressions available in ShockScript.

Wildcard type

The * type is dynamically typed and consists of all possible values in other types.

void type

The void type consists of the undefined value.

null type

The null type consists of the null value.

string type

The string type represents an UTF-8 encoded character sequence.

Note: the .length property of a string equals the byte total, and the .chars().length() method of a string equals the Unicode code point total.

boolean type

The boolean type consists of the values false and true.

float type

The float type represents an IEEE 754 single-precision floating point.

double type

The double type represents an IEEE 754 double-precision floating point.

decimal type

The decimal type represents an arbitrary range decimal number.

int type

The int type represents a signed 32-bit integer.

uint type

The uint type represents an unsigned 32-bit integer.

bigint type

The bigint type represents an arbitrary range integer.

Array type

The Array.<T> type, abbreviated [T], represents a growable list of elements.

Numeric optimization

[T] is optimized for when T is a number type; for instance, [uint] will use a growable buffer optimized specifically for 32-bit unsigned integers.

Map type

The Map.<K, V> type represents a hash map.

Map.<K, V>

Note: Property access on a Map equals data access. Method call on a Map equals a Map method use.

Instance usage

const map = new Map.<string, double>();

// x=10
map.x = 10;

const fns = new Map.<string, Function>();

// m=function
fns.m = function() 10;

// m()
trace((fns.m)());

Tuple types

Tuple types, of the form [T1, T2, ...TN], are immutable sequences consisting of known element types at compile time. A tuple type contains at least two elements.

[boolean, float]

Function types

Structural function types inherit from the Function class, taking the form function(...) : T.

function(T1, T=, ...[T]):T

A structural function type may specify:

  • zero or more required parameters (T) followed by
  • zero or more optional parameters (T=) followed by
  • a rest parameter (...[T]),
  • and a result type.

Record types

Record types { ... } are simple property records. Record types are memory-efficient in cost of a hash-map-like field access.

Note: The decision for having record types compiling into hash-map structures is because these types use to contain many fields, for instance, in WhackDS components and in operations requiring a large number of options.

type N1 = { x : decimal, y : decimal };

type N2 = {
    /** x */
    x:double,

    /** y */
    y?:boolean,
};

Note: Record types do not match with types structurally unlike in structural-type-first languages. They are simply structures the user may express inline.

Version control

Fields of a record type may be tied to a namespace, which is useful for version control.

// ===== Flexible.es =====

package = com.example.product;

/**
 * Flexible version control namespace.
 */
public namespace Flexible = "http://business.com/product/flexible";

// ===== Judgement.es =====

package = com.example.product;

/**
 * Judgement version control namespace.
 */
public namespace Judgement = "http://business.com/product/judgement";

// ===== Pair.es =====

package = com.example.product;

/**
 * Pair.
 */
public type Pair = {
    Flexible::strength : [decimal],
    Judgement::strength : [decimal],
};

Field omission

All fields are required unless they contain undefined or null. A field such as x?:T is equivalent to x:(void, T).

Field order

Due to sensitive field order, record types with equivalent fields but in different orders will be incompatible.

Writing ESDoc comments

Fields may have a preceding ESDoc comment, as in:

type R = {
    /**
     * Comment.
     */
    x : double,
};

Compatibility

Two record types are compatible only if either a) one is used as a subset of another or b) fields are equivalent and appear in the same order.

Rest

One trailing ...rest component may appear in a record, where rest must be another record type. The resulting type is a subtype of rest and properties must not collide.

// A
type A = { x:double };
// B < A
type B = { y:double, ...A };

Union types

The structural union type, written (m1, m2, ...mN), consists of two or more member types, containing all possible values of the member types.

(decimal, string)

Restrictions

  • Unions contain two or more members.

Default value

The default value of an union type is determined as follows:

  1. If it contains void, then undefined.
  2. If it contains null, then null.
  3. No default value.

Nullability

The following shorthands are available for nullability:

  • T? or ?T is equivalent to (null, T).
  • T! removes null and/or void from T.

Object type

All types but { void, null, uint, int, float, double, decimal, bigint, boolean, string } represent referenceable objects. The Object class is inherited by all types but { *, void, null, union }.

Note: When it is necessary to obtain the constructor of an object, use:

obj.meta::class()

Conversions

This section describes which type conversions are available.

Explicit conversions may occur as either T(v) (strict conversion) or v as T (optional conversion). The behavior of the call operator over a type may not always be a conversion depending on if T implements the static meta::call() hook.

v as T     // returns T?. failure returns null
v as! T    // failure throws a TypeError (as-strict)
T(v)       // same as "v as! T"

Constant coercions

Constant coercions occur implicitly both at compile-time and runtime, converting a constant into another constant.

KindResult
undefined to flag enumerationInterned instance whose value is zero (0).
null to flag enumerationInterned instance whose value is zero (0).
undefined to T containing both undefined and nullundefined
undefined to T containing undefined and no nullundefined
undefined to T containing null and no undefinednull
null to T containing undefined but not nullundefined
null to T containing null but not undefinednull
null to T containing both undefined or nullnull
Numeric constant to * or ObjectEquivalent constant of the target type.
String constant to * or Object or union containing stringEquivalent constant of the target type.
Boolean constant to * or Object or union containing booleanEquivalent constant of the target type.
Namespace constant to * or Object or union containing NamespaceEquivalent constant of the target type.
Type constant to * or Object or union containing ClassEquivalent constant of the target type.
Numeric constant to another compatible numeric typeNumeric constant with value coerced to target type.
Numeric constant to union containing at least one compatible numeric typeNumeric constant of the target type containing value coerced to the containing numeric type, preferring the same numeric type or otherwise the first numeric type found.
NaN to floatNaN
NaN to decimalNaN
-Infinity to float-Infinity
+Infinity to float+Infinity
-Infinity to decimal-Infinity
+Infinity to decimal+Infinity

Implicit coercion

Implicit coercions occur implicitly both at compile-time and runtime, after trying a constant coercion.

KindResult
From *
To *
From numeric type to compatible numeric type
To covariant (includes base classes, implemented interfaces, unions and inherited record type)
From union to compatible union
From union member to union
From structural function type to another compatible function type

Note: interface types inherit Object.

Explicit conversions

Explicit conversions occur when resolving v as T or T(v), after trying an implicit coercion.

KindResult
To contravariant (from interface to interface subtype, from class to subclass, or record type subtype)
To union member
From * or Object to interface
To another [T] typeAn array filtering out incompatible elements.
string to enumerationIdentification of an enumeration variant by its string name.
Number to enumeration (using the same numeric type)For regular enumerations, identifies a variant by its numeric value. For flag enumerations, identifies variant bits.
To stringFor undefined, returns "undefined"; for null, returns "null"; for other types, invokes toString().
To booleanEvaluates truthy value.
To doubleForced conversion to double-precision floating point.
To floatForced conversion to single-precision floating point.
To decimalForced conversion to 128-bit decimal number.
To intForced conversion to 32-bit signed integer.
To uintForced conversion to 32-bit unsigned uninteger.
To bigintForced conversion to an arbitrary range integer.
Record type into equivalent record type of non-uniform field order
From type parameter

Note: interface types inherit Object.

Property lookup

LookupKey

LookupKey is either LocalName(name) or Computed(value).

LookupKey.Value

LookupKey.Value returns:

  • For LocalName(name), a StringConstant equivalent to name or defer if string is unresolved.
  • For Computed(value), value.

LookupKey.Type

LookupKey.Type returns:

  • For LocalName(name), the string type or defer.
  • For Computed(value), the static type of value or defer.

LookupKey.Double

LookupKey.Double returns:

  • For LocalName(name), undefined.
  • For Computed(value), value is NumberConstant(v) ? force convert v to a double : undefined.

PropertyLookup()

PropertyLookup(base, openNsSet, qual, key as LookupKey, followedByCall as boolean, fixed as boolean) takes the following steps in order, where fixed allows forcing access to a fixture property on dynamic types (used by the <?fixed={}?> expression):

  • If base is invalidation
    • Return invalidation
  • Let localName = key is LocalName(name) ? name : undefined
  • Let doubleKey = key.Double
  • If base is a TypeConstant(type)
    • base = type
  • Else if base is a FixtureReferenceValue and base.Property is a type
    • base = base.Property
  • If base is a class
    • If localName is undefined or (qual is specified and qual is not a namespace nor a NamespaceConstant)
      • Return StaticDynamicReferenceValue(base, qual, k.Value)
    • For each descending class in base hierarchy
      • Defer if class is unresolved.
      • Let r = GetQNameInNsSetOrAnyPublicNs(class static properties, openNsSet, qual, localName)
      • If r != undefined
        • Mark r as used.
        • Let r = r.ResolveAlias()
        • Defer if r property’s static type is unresolved.
        • Return r.Wrap()
    • Return undefined
  • If base is an interface
    • Return undefined
  • If base is a value
    • Let baseType = static type of base or defer
    • If baseType is invalid
      • Return invalid
    • baseType = baseType.ResolveAlias()
    • If (followedByCall == false and fixed == false) and baseType defines an instance method meta::get (possibly a multi method)
      • Let foundRegularProperty = false
      • For each meta::get(k:K):V method
        • If qual != undefined
          • If K == * or K == (Object or defer) or K == (QName or defer) or K ?union contains (QName or defer)
            • Return KeyValuePairReferenceValue(base, meta-method, qual as a Namespace object, key.Value coerced to (string or defer))
          • Continue loop
        • If key.Value is a (string or defer) value and (K == (QName or defer) or (K ?union does not contain string and K union contains QName))
          • Return KeyValuePairReferenceValue(base, meta-method, undefined, key.Value)
        • If K == * or K == (Object or defer) or K == (string or defer) or K == (QName or defer) or (K ?union contains string or K union contains QName)
          • foundRegularProperty = true
        • If (static type of key.Value or defer) fails on implicit coercion to K
          • Continue loop
        • Return KeyValuePairReferenceValue(base, meta-method, undefined, key.Value implicitly coerced to K)
      • If (static type of key.Value or defer) != (string or defer) or foundRegularProperty
        • Throw a verify error
    • Let hasKnownNs = qual == undefined or (qual is a namespace or NamespaceConstant)
    • If localName == undefined
      • If doubleKey != undefined and baseType is a tuple
        • Let i = doubleKey coercion to integer
        • If i < 0 or i >= baseType.ElementTypes.Length
          • Throw a verify error
        • Return TupleReferenceValue(base, i)
      • Return DynamicReferenceValue(base, qual, key.Value, followedByCall, fixed)
    • If baseType == *
      • Return DynamicReferenceValue(base, qual, key.Value, followedByCall, fixed)
    • If baseType is a class
      • For each descending class in baseType hierarchy
        • Defer if class is unresolved
        • Let prop = GetQNameInNsSetOrAnyPublicNs(class prototype properties, openNsSet, qual, localName)
        • If prop != undefined
          • Mark prop as used
          • prop = prop.ResolveAlias()
          • Call prop.Defer() (if about to defer, implementation may report the cause as unresolved expression in a location)
          • If prop is a namespace or NamespaceConstant
            • Return NamespaceConstant(prop) if prop is a namespace, or otherwise prop as is
          • Return InstanceReferenceValue(base, prop)
      • For implemented interfaces of baseType
        • Lookup method (step required for optional methods)
    • Else if baseType is an interface
      • For each descending itrfc in baseType hierarchy
        • Defer if itrfc is unresolved
        • Let prop = GetQNameInNsSetOrAnyPublicNs(itrfc prototype properties, openNsSet, qual, localName)
        • If prop != undefined
          • Mark prop as used
          • prop = prop.ResolveAlias()
          • Call prop.Defer() (if about to defer, implementation may report the cause as unresolved expression in a location)
          • Return InstanceReferenceValue(base, prop)
      • Lookup for the Object instance definitions
    • Return undefined.
  • If base is a package
    • If localName is undefined or (qual is specified and qual is not a namespace nor a NamespaceConstant)
      • Throw a verify error
    • Let r = undefined
    • Let prop = GetQNameInNsSetOrAnyPublicNs(base properties, openNsSet, qual, localName)
    • If prop != undefined
      • Mark r as used.
      • prop = prop.ResolveAlias()
      • Call prop.Defer() (if about to defer, implementation may report the cause as unresolved expression in a location)
      • r = prop.Wrap()
    • Return r
  • If base is a type parameter with a Limit("... extends Event(...)") constraint
    • If localName is undefined or (qual is specified)
      • Return undefined.
    • If localName = name
      • Return EventNameType(base type parameter).Wrap()
    • If localName = type
      • Return EventTypeType(a previously introduced EventNameType).Wrap().
    • Return undefined.
  • Return undefined.

Note: entity.Wrap() wraps entities into values. For instance, wrapping a variable into a property reference, where it belongs to a package, will produce a PackageReferenceValue.

Note: entity.Defer() defers if an entity is unresolved or if a direct compound (like an alias resolution) is unresolved.

InScopeLookup()

InScopeLookup(scope, qual, key as LookupKey, followedByCall as boolean, fixed as boolean) takes the following steps in order:

Note: Content lacking.

Packages

A package consists of a name (typically a reverse domain name), a set of properties and two namespaces, public and internal.

A package com.business.enum is expressed as:

package = com.business.enum;

//

Or applied to a program block:

package com.business.enum {
    //
}

Note: One common convention is for packages to use a prefix domain (one of ( com, net, org, me )); alternatively an user may use a prefixless domain name (such as skia rather than com.google.skia). The me prefix is used for personal content and the rest for companies, organizations and groups.

The user defines properties under the package inside the package block, as in:

If using the package; or package = ... pragma, any definition in the program belongs to the package:

package = f.q;

public function f():void {}

Or when using a block:

package f.q {
    public function f():void {}
}

Top-level package

The top-level package, which defines global properties, is equivalent to:

package;

//

Or, alternatively:

package {
    //
}

When a global name is shadowed, the user may use the ES4 namespace to lookup a global name:

Math
    // or
ES4::Math

For defining a custom alias to the top-level package, define a namespace with the URI http://www.sweaxizone.com/2015/shockscript/global, as in:

namespace ES4 = "http://www.sweaxizone.com/2015/shockscript/global";

Name shadowing

It is possible to fully qualify a name in an expression using a package and one of its items, shadowing any other variables.

import org.colourful.color.Color;
var com = 0;
trace( org.colourful.color.Color(0x10_00_00) );
trace( Color(0x10_00_00) );

ReadMe

A directory identifying a package relative to a source path may contain a README file (either README or README.md) written as Markdown text, which serves as a means to attach documentation to the package.

Package single import

A package single import is contributed to the lexical scope for the following directive:

import f.q.x;

Package wildcard import

A package wildcard import is aliased for the following directive:

import q = com.business.quantum.*;

Then q may be used as a qualifier to resolve to a name in the com.business.quantum.* package (excluding subpackages).

q::x

For the following directive, the package wildcard import is contributed to the lexical scope:

import com.business.quantum.*;

Source path

  • A package definition must contain exactly one definition item, and its name must match the source path.
  • A source file must consist of exactly one package definition.

Namespaces

Names are three-dimensional, consisting of a namespace (the qualifier) and a local name. The :: punctuator is used in qualified identifiers for using a namespace prefix and a local name, as in:

q::n
o.q::n

Namespaces appear as optional access modifiers in annotatable definitions, as in:

special_properties var acc:decimal = 0;

There are reserved namespaces and user namespaces.

Reserved namespaces

Reserved namespaces (or system namespaces) are created implicitly by the language:

  • public
  • internal
  • protected
  • private
  • static protected
  • meta

They are tied to a parent (such as a package, a class or a scope), except in the case of meta.

A namespace definition that omits the URI creates an internal namespace:

namespace special_properties;

User namespaces

User namespaces are identified by an URI.

namespace observer_internals = "http://observer.net/internals";

Classes

A class is an inheritable user-defined type that may be used to create objects.

class C1 {
    //
}
const obj = new C1();

Inner namespaces

A class owns three namespaces:

  • private
  • protected
  • static protected

protected and static protected are propagated to the block of subclasses.

ESDoc comment

A class may be prefixed by a ESDoc comment.

/** Comment */
class C1 {}

Meta-data

A class may have zero or more meta-data.

[M1]
[M2]
class C1 {}

Inheritance

class A {}
class B extends A {}

Member shadowing

Members from base classes must not be shadowed except for overriding methods.

class C1 {
    function m() {}
}
class C2 extends C1 {
    function m() {} // ERROR!
}

Default inheritance

By default a class, excluding Object, inherits Object. A class can extend at most one class.

Final classes

A final class may not be extended:

final class A {}

class B extends A {} // ERROR!

Implementing interfaces

A class may implement zero or more interfaces:

class C1 implements I1, I2 {
    //
}

Constructor inheritance

If the constructor of a class is not explicitly defined, then it is based on the base class’s constructor, using the same signature and initializing the instance with default field values:

class A {
    var x:double;
    function A(x:double) {
        this.x = x;
    }
}
class B extends A {
    var y:double = 10;
}
new B(0);
new B(); // ERROR!

Constructor

The constructor of a class is a special initialization method named as the class itself, as in:

class C1 {
    function C1() {}
}

Super statement

The super statement is used to invoke the constructor from the base class from a subclass constructor.

A constructor must contain the super statement if a class is inherited which consists of a required constructor.

class A {
    function A(x:double) {}
}
class B extends A {
    function B() {
        super(0);
    }
}

Abstract classes

An abstract class may not be directly instantiated through the new operator, and may define abstract methods. Non abstract subclasses are allowed to be instantiated.

abstract class A {
    abstract function m():void;
}

Static classes

A static class may not be instantiated or inherited, and by convention consists of static properties and methods.

static class MyNamespace {
    static const VALUE:double = 10.5;
}

Events

The class, in convention when either extending EventTarget or implementing IEventTarget, may define possibly emitted events through using multiple Event meta-data.

/**
 * Event.
 */
[Event(name="eventName", type="T")]
/**
 * Target.
 */
class A extends EventTarget {}

Static properties

Definitions marked static that appear within the class block are part of the static properties of the class, which are accessed as C.n where C is the class and n the property name.

Instance properties

Definitions not marked static that appear within the class block are part of the prototype of the class, and are called instance properties.

Nested classes

ShockScript supports nested classes.

Note: The initial decision for supporting nested classes is that they allow reducing the number of ShockScript source files when a programmer attempts to express a type close to an algebraic data type consisting of many variants.

Nested classes, due to the scope, may access both protected and private members of the enclosing classes.

//
static class Outer {
    //
    class Inner {
        //
    }
}

Enumerations

Enumerations are special classes consisting of zero or more variants.

enum Variant {
    const VAR_ONE;
    const VAR_TWO = "var_two";
    const VAR_THREE = [2, "var_three"];
}

Note: Variable definitions within an enum define static constants which are referred to as variants.

Final

Enumerations are final, so they cannot be extended.

Static

Enumerations are static, so they cannot be instantiated through the new operator.

Type inference

When the inference type in a string literal is an enumeration, the literal may identify a variant by its name.

var val : Variant = "var_one";

When the inference type in an array literal or object initializer is a flag enumeration, the literal may be used to identify multiple variants.

[Flags]
enum F { const A, B, C }

const m:F = ["a", "b", "c"];
    // or
const m:F = { a: true, b: true, c: true };

Flag enumerations

Flag enumerations differ from regular enumerations by having instances being able to contain zero or more variants.

[Flags]
enum F { const A, B, C }

Flag enumerations may be assigned undefined, null or [] to indicate absence of variants.

When a flag enumeration’s numeric type is a floating point, the values are internally cast between a unsigned 32 bit integer and that floating point’s type; therefore their range may be less than what the floating point supports.

All variants

Obtain all variants of a flag enumeration by using the ** expression with the enumeration as the inference type:

var f:F = **;

Internation

Flag enumeration objects are interned so that flags may be compared correctly.

[Flags]
enum E { const A, B, C }

const obj:* = E(["a", "b"]);
trace(obj == E(["a", "b"]));

Customizing the numeric type

Enumerations use the uint type by default to represent the variant values. The user is allowed to change the type to another numeric type through using a meta-data named after that numeric type.

[decimal]
enum E1 {
    const A, B, C;
}

Variant initializer

The initializer of a variant may be expressed in four different forms, or simply be omitted:

StringLiteral
NumericLiteral
[StringLiteral, NumericLiteral]
[NumericLiteral, StringLiteral]

The ArrayLiteral syntax is used to allow specifying both a string and a number.

Variant name

The variant name as declared by the const is determined as follows:

  1. Let r = empty string
  2. If the initializer does not contain a string literal
    1. Let orig = binding identifier name
    2. r = conversion of orig to lowercase.
  3. Else
    1. r = the value of the string literal at the initializer.
  4. If r is already used by another variant’s name
    1. Throw a verify error
  5. Return r

Variant value

The variant value as declared by the const is determined as follows:

  1. If the enumeration is a flag enumeration 2. Return DecideFlagValue()
  2. Return DecideValue()

DecideValue()

  1. Let r = zero
  2. If the initializer does not contain a numeric literal
    1. If there is no previous variant, return 0.
    2. Let k = previous variant’s value
    3. r = k + 1
  3. Else
    1. r = the value of the numeric literal at the initializer.
  4. If r is already used by another variant’s value
    1. Throw a verify error
  5. Return r

DecideFlagValue()

  1. Let r = zero
  2. If the initializer does not contain a numeric literal
    1. If there is no previous variant, return 1.
    2. Let k = previous variant’s value
    3. r = k * 2
  3. Else
    1. r = the value of the numeric literal at the initializer.
    2. If r is not one or a power of two
      1. Throw a verify error
  4. If r is already used by another variant’s value
    1. Throw a verify error
  5. Return r

Implicitly added methods

For all enumerations

valueOf()

override function valueOf():T {
    //
}

Returns the numeric value of the enumeration instance, where T is the numeric type.

toString()

override function toString():string {
    //
}

Returns the name of the enumeration instance. For a flag enumeration, returns the names of the enumeration instance delimited by comma (,) by ascending value order.

For flag enumerations

meta::has()

meta function has(v:E):boolean {
    //
}

Returns a boolean indicating whether the instance contains the specified flags or not, where E is the enumeration itself.

This allows for f in e expressions.

with()

function with(v:E):E {
    //
}

Returns a new value containing the specified flags, where E is the enumeration itself.

without()

function without(v:E):E {
    //
}

Returns a new value removing the specified flags, where E is the enumeration itself.

toggled()

function toggled(v:E):E {
    //
}

Returns a new value toggling the specified flags, where E is the enumeration itself.

Customized methods

Enumerations support customized methods:

enum E {
    const A, B, C;
    function get isA() this == "a";
}

Enumerations are prohibited from using variable definitions for purposes other than defining variants.

Interfaces

Interfaces are user defined, non opaque types that may be implemented by classes through their implements clause.

interface I {
    //
    function m() : void;

    //
    function get x() : double;
    function set x(value);
}

interface Ia extends I {}

The interface block may only contain function definitions, including regular methods, getters and setters.

Basemost type

An interface is a subtype of Object, although compile-time property lookups do not inherit Object properties.

ESDoc comment

An interface may be prefixed by a ESDoc comment.

/** Comment */
interface I {}

Meta-data

An interface may have zero or more meta-data.

[M1]
[M2]
interface I {}

Inheritance

An interface may extend other interfaces through the extends clause.

interface I3 extends I1, I2 {}

Shadowing members

Members from base interfaces must not be shadowed.

interface I1 {
    function m() {}
}
interface I2 extends I1 {
    function m() {} // ERROR!
}

Required methods

When interface methods omit their body, they are classified as required methods.

interface I {
    function m():void;
}

Provided methods

When interface methods contain a body, they are classified as provided methods.

interface I {
    function m() {
        //
    }
}

Method annotations

As annotations, interface methods may have nothing but an access modifier that is allowed to be anything but a direct reserved namespace.

interface I {
    meta function get(key:string):string;
}

Events

The interface, in convention when implementing IEventTarget, may define possibly emitted events through using multiple Event meta-data.

/**
 * Event.
 */
[Event(name="eventName", type="T")]
/**
 * Target.
 */
interface I extends IEventTarget {}

Remote classes

Classes annotated with the Remote meta-data may be serialized or deserialized by JSON and possibly other user types.

TODO

Variables

A variable may be read-only or writeable, and consists of a type.

var x = 0
const y = 10

ESDoc comment

A ESDoc comment can be applied to a variable.

/** Comment */
var x

Meta-data

A variable may have zero or more meta-data.

[M1]
[M2]
var x

Initializer

If the initializer of a variable is a constant, then the variable consists of a constant initializer.

var x = 0

Local shadowing

Re-declaring a variable is allowed inside activation blocks.

var m:* = central.manager;
// more code...
var m = Manager(m);

Virtual variables

Virtual variables consist of either:

  • a getter and a setter (writable);
  • a getter (read-only);
  • a setter (write-only).

A virtual variable’s type is determined based on the getter or setter.

function get x():float 10;
function set x(val) {
    //
}

ESDoc comment

A virtual variable derives ESDoc comments from its getter or setter.

/** Comment */
function get x():float 10;

Meta-data

A virtual variable collects meta-data from its getter or setter.

[M1]
[M2]
function get x():float 10;

Methods

A method is a function that may be invoked. An instance method is a method defined in a class, enum or interface block which is not marked static.

function m() {}

Getters and setters are methods belonging to a virtual variable:

function get x():decimal 10;
function set x(val) {}

Constructors are methods that implement initialization for a class instance, as in:

class A {
    function A() {}
}

ESDoc comment

A ESDoc comment can be applied to a method.

/** Comment */
function m() {}

Meta-data

A method may have zero or more meta-data.

[M1]
[M2]
function m() {}

Final method

Instance methods may have a final modifier, indicating that they are not to overriden by subclasses.

class A {
    final function m() {}
}

Abstract method

Instance methods may have an abstract modifier under an abstract class, indicating that they must be overriden by subclasses.

abstract class A {
    abstract function m():void;
}

Generators

A method is a generator if the yield operator appears at least once in the method’s body. A generator is a method that evaluates like an iterator, consumed in pauses of yield operators until it hits a return statement or the end of code. A generator returns a Generator.<T> object.

function g():double {
    yield 100.5;
}

If a method uses both yield and await, it is considered an iterator of Promise, therefore returning Generator.<Promise.<T>>.

Asynchronous methods

A method is asynchronous if the await operator appears at least once in the method’s body. An asynchronous method returns a Promise.<T> object.

function f():void {
    await otherF();
}

If a method uses both yield and await, it is considered an iterator of Promise, therefore returning Generator.<Promise.<T>>.

Multi-methods

By using a generic-annotated header method, a method may be defined more than once with varying signatures, turning into a multi-method. Signatures must differ by the parameter list and not just the result type.

A generic header method must consist of exactly an untyped rest parameter and must omit the result type. Its purpose is to declare that a method is a multi-method and may be re-defined multiple times in the same scope or in the same directly-enclosing class.

generic function f(...);

function f():decimal {
    // code
}
function f(val:decimal):Chainable {
    // code
}

Overriding

An instance method may override a method in a base class through using the override modifier:

override protected function m() {
    //
}

Restrictions

  • A getter must override a getter, and a setter must override a setter.
  • For a multi method, the override shall match a specific signature.
  • It is not allowed to have a generic-annotated function that overrides another function (whether generic or not).

Overriding rules

A method S may override a method B with the following rules:

  • S must begin with the same list of parameters as that of B.
  • If B does not contain a rest parameter
    • S may include additional optional parameters and/or a rest parameter.
  • S must have the same result type of B, or a subtype of the B result type.

The above overriding rules apply to non-multi-methods; for multi methods, the override signature must be exactly the same.

Bound methods

Instance methods are bound such that retrieving a method from an instance will return a method tied to the instance.

class A {
    function m():A {
        return this;
    }
}
const o = new A;
const { m } = o
trace(m == o.m); // true
trace(o == m());

Aliases

Aliases are used in different places of the language:

import CT = com.business.coreRT.enum.ContactType;
import q = com.business.quantum.*;
type U = (decimal, string);
namespace special_version;

ESDoc comment

An alias may be prefixed by a ESDoc comment.

/** Comment */
type Params = {
    x : decimal,
};

Meta-data

Meta-data are bracketed, arbitrary entries of textual key-value pairs that may be attached to ShockScript definitions. Meta-data are not unique and may appear more than once, as well as their key-value pairs.

[M1]
class A {}

[M1(x="y", z="w")]
class A {}

[M1(y)]
class A {}

Keyless entries are a single identifier (equivalent to a string) or a string literal not accompanied by a key.

Reserved meta-data

Certain meta-data are reserved in certain contexts, such as Event and Limit.

Generics

Classes, interfaces, type aliases and functions may specify type parameters, turning into generic entities. ShockScript implements generic entities using polymorphism.

Note: Array data types of double, float, decimal, int and uint are specialised so they are represented in a memory efficient way.

class A.<T> {
    // code
}
interface I.<T> {
    // code
}

type Alias.<T> = (decimal, [T]);

function f.<T>():void {
}

Parameter constraints

Type parameters may be attached multiple constraints.

[Limit("T extends A, B")]
/**
 * Some function.
 */
function f.<T>(o:T) {
    //
}

[Limit("E extends Event(A)")]
/**
 * Another function.
 */
function f.<E>(type:E.name, value:E.type) {
    //
}

[Limit("E extends Event*(A)")]
/**
 * Yet another function.
 */
function f.<E>(value:E) {
    //
}

Event() constraints

Event() constraints allow inspecting available events as defined by the Event meta-data in classes and interfaces, including the inherited events and events from the implemented interfaces.

Event() constraints are allowed to take this as the base type, reflecting the current class’s events:

package = com.business.coreRT.events;

/**
 * Event dispatcher.
 */
public class EventTarget {

    [Limit("E extends Event(this)")]
    /**
     * Dispatches an event.
     */
    public function emit.<E>(e:E):boolean {
        //
    }
}
  • Event() yields the name-type pair of an event. The .type property of the pair relies on previous introduction of the respective .name somewhere.
  • Event*() ensures event creation is correct by analyzing the new E(type, ...) expression.

Note: The Event() constraint contributes a name field that yields the string type, but its purpose is for auto completion in integrated development environments.

Lexical scopes

Internal properties

NameDescription
[[Parent]]Optional parent scope.
[[OpenNamespaces]]Open namespace list.
[[Properties]]The scope properties.
[[Imports]]The import list.

Import list

The import list may contain package single imports and package wildcard imports.

Scope variants

Class scope

Added internal properties

NameDescription
[[Class]]Class object.

Enum scope

Added internal properties

NameDescription
[[Class]]Class object.

Interface scope

Added internal properties

NameDescription
[[Interface]]The interface.

Package scope

Added internal properties

NameDescription
[[Package]]Package.

Activation

Method bodies create an activation as scope.

Added internal properties

NameDescription
[[This]]The this object.
[[Method]]Method.

Implicit lexical scope

The topmost scope from which all scopes inherit is implicitly created by ShockScript.

Imports

The topmost scope imports the top-level package by wildcard. It is allowed to shadow names from the top-level package, in which case, the ES4 alias may be used to access the top-level package.

ES4

The top-level package defines an ES4 property, which is an alias to a package wildcard import of the top-level package.

Intl

The top-level package defines an Intl property, which is an alias to a package wildcard import of the org.shockscript.intl package.

Temporal

The top-level package defines an Temporal property, which is an alias to a package wildcard import of the org.shockscript.temporal package.

Conditional compilation

The NAMESPACE::constant expression may match a configuration constant used for conditional compilation.

NAMESPACE::constant {
    //
}

NAMESPACE::constant var x

The following program uses an inline constant.

trace(NAMESPACE::constant)

Implicit configuration constants

ES4::debugging

The ES4::debugging constant indicates whether or not the program is targetting a debug build.

ES4::releasing

The ES4::releasing constant indicates whether or not the program is targetting a release build.

ES4::testing

The ES4::testing constant indicates whether or not the program is compiling for evaluating unit tests.

Unit testing

Basic unit testing may be done by defining functions annotated with the Test meta-data at a package-level.

package = com.example.foo.tests

[Test]
public function foo() : void {
    assert.equal(2 + 2, 4)
}

Or the user may define a class annotated with the Test meta-data at a package-level, consisting of an empty constructor and instance methods annotated with the Test meta-data.

package = com.example.foo.tests

[Test]
public class Tests {
    [Test]
    public function foo() : void {
        //
    }
}

ESDoc comments

ESDoc are documentation comments that use the format /** */. Markdown notation is supported in ESDoc comments.

For each line, the beginning whitespace is stripped, then the * character and a single following white space character are stripped, and the resting characters are the actual line contents.

Line contents may start with a tag (such as @deprecated). Tags may span multiple lines until the next tag appears; tags that do not accept content do not span any more characters.

Code blocks (whose delimiters consist of at least three backticks ```) as expressed in Markdown cause tags to be ignored in the code content, as in:

/**
 * ```plain
 * @deprecated
 * ```
 */

Local images

ESDoc comments may refer to relative images through the Markdown notation ![image](./path/to/image).

Supported tags

@copy

Copies ESDoc comment from another definition. Use a #x component to refer to an instance property.

@copy A
@copy A.w
@copy A#x
@copy #x

@default

Default value as an uninterpreted expression.

@default exp

@deprecated

@deprecated [Description]

@example

@example
The following...

@event

Indicates that a record type’s property is an event handler. The tag does nothing but move the item to the Events section of the documentation, similiar to how happens with the Event meta-data used in class definitions.

@event

@inheritDoc

Inherits documentation from base class or base class’s item.

@inheritDoc

@internal

Internal comment for an item (not included in the generated documentation).

@internal Comment.

@param

@param paramName Description

@private

Hides an item from the generated documentation.

@private

@return

@return Description

@see

Where item maybe an item reference with optional #x instance property, or just an instance property #x.

@see item [Display text]

@throws

@throws ClassName [Description]

Meta-methods

Certain methods of the meta namespace may be implemented in a class or interface for overriding language behavior; those are referred to as meta-methods.

meta::call()

A static meta::call() method may be defined with any number of parameters and any result type, overriding the behavior of calling the class object.

meta static function call():T {}

A multi-method may be used, allowing for multiple call signatures.

meta::get()

Note: Overriding the property accessor with a possibly string or QName key type (including base types * and Object) will override all names (like .x), except when calling a method (like .m()). In that case, a class is said to be dynamic.

If the user needs to access an instance variable in such a case it is recommended to use a fixed expression as in

<?fixed={object.q::n}?>
meta function get(key:K):V {
    //
}

meta::set()

Note: Overriding the property accessor with a possibly string or QName key type (including base types * and Object) will override all names (like .x), except when calling a method (like .m()). In that case, a class is said to be dynamic.

meta function set(key:K, value:V):void {
    //
}

meta::delete()

Note: Overriding the property accessor with a possibly string or QName key type (including base types * and Object) will override all names (like .x), except when calling a method (like .m()). In that case, a class is said to be dynamic.

meta function delete(key:K):boolean {
    //
}

meta::has()

Overrides the behavior of the in operator.

meta function has(key:K):boolean {
    //
}

meta::getAttribute()

Overrides the behavior of the .@k accessor.

meta function getAttribute(key:K):V {
    //
}

meta::setAttribute()

Overrides the behavior of the .@k = v accessor.

meta function setAttribute(key:K, value:V):void {
    //
}

meta::deleteAttribute()

Overrides the behavior of the delete (...).@k accessor.

meta function deleteAttribute(key:K):boolean {
    //
}

meta::filter()

Overrides the behavior of the filter operator (.(test)).

meta function filter(testFn:function(T):boolean):E {
    //
}

meta::descendants()

Overrides the behavior of the descendants operator (..x). The parameter is expected to be typed string or QName.

meta function descendants(name:QName):E {
    //
}

Lexical conventions

This section defines the lexical grammar of the ShockScript language.

The tokenizer scans one of the following input goal symbols depending on the syntactic context: InputElementDiv, InputElementRegExp, InputElementXMLTag, InputElementXMLPI, InputElementXMLContent.

The following program illustrates how the tokenizer decides which is the input goal symbol to scan:

/(?:)/       ;
a / b        ;
<a>Text</a>  ;

The following table indicates which is the input goal symbol that is scanned for each of the tokens comprising the previous program:

TokenInput goal
/(?:)/InputElementRegExp
;InputElementDiv
aInputElementRegExp
/InputElementDiv
bInputElementRegExp
;InputElementDiv
<InputElementRegExp
aInputElementXMLTag
>InputElementXMLTag
TextInputElementXMLContent
</InputElementXMLContent
aInputElementXMLTag
>InputElementXMLTag
;InputElementDiv

The InputElementXMLPI goal symbol must be used when parsing the <?fixed={exp}?> markup.

Syntax

    InputElementDiv ::
      WhiteSpace
      LineTerminator
      Comment
      Identifier
      ReservedWord
      Punctuator
      /
      /=
      NumericLiteral
      StringLiteral
    InputElementRegExp ::
      WhiteSpace
      LineTerminator
      Comment
      Identifier
      ReservedWord
      Punctuator
      NumericLiteral
      StringLiteral
      RegularExpressionLiteral
      XMLMarkup
      <?fixed={
    InputElementXMLTag ::
      XMLName
      XMLTagPunctuator
      XMLAttributeValue
      XMLWhitespace
      {
    InputElementXMLPI ::
      ?>
    InputElementXMLContent ::
      XMLMarkup
      XMLText
      {
      < [lookahead ∉ { ?, !, / }]
      </

Source Characters

Syntax

    SourceCharacter ::
      Unicode code point
    SourceCharacters ::
      SourceCharacter SourceCharactersopt

White Space

The WhiteSpace token is filtered out by the lexical scanner.

Syntax

    WhiteSpace ::
      U+09 tab
      U+0B vertical tab
      U+0C form feed
      U+20 space
      U+A0 no-break space
      Unicode “space separator”

Line Terminator

The LineTerminator token is filtered out by the lexical scanner, however it may result in a VirtualSemicolon to be inserted.

Syntax

    LineTerminator ::
      U+0A line feed
      U+0D carriage return
      U+2028 line separator
      U+2029 paragraph separator

Comment

The Comment token is filtered out by the lexical scanner, however it propagates any LineTerminator token from its characters.

/*
 * /*
 *  *
 *  */
 */

Syntax

    Comment ::
      // SingleLineCommentCharacters
      MultiLineComment
    SingleLineCommentCharacters ::
      SingleLineCommentCharacter SingleLineCommentCharactersopt
    SingleLineCommentCharacter ::
      [lookahead ∉ { LineTerminator }] SourceCharacter
    MultiLineComment ::
      /* MultiLineCommentCharactersopt */
    MultiLineCommentCharacters ::
      SourceCharacters [but no embedded sequence /*]
      MultiLineComment
      MultiLineCommentCharacters SourceCharacters [but no embedded sequence /*]
      MultiLineCommentCharacters MultiLineComment

Virtual Semicolon

The VirtualSemicolon nonterminal matches an automatically inserted semicolon, known as a virtual semicolon.

Virtual semicolons are inserted in the following occasions:

  • After a right-curly character }
  • Before a LineTerminator

Identifier

The Identifier symbol is similiar to that from the ECMA-262 third edition, but with support for scalar Unicode escapes.

Syntax

    Identifier ::
      IdentifierName [but not ReservedWord or ContextKeyword]
      ContextKeyword
    IdentifierName ::
      IdentifierStart
      IdentifierName IdentifierPart
    IdentifierStart ::
      UnicodeLetter
      underscore _
      $
      UnicodeEscapeSequence
    IdentifierPart ::
      UnicodeLetter
      UnicodeCombiningMark
      UnicodeConnectorPunctuation
      UnicodeDigit
      underscore _
      $
      UnicodeEscapeSequence
    UnicodeLetter ::
      Unicode letter (“L”)
      Unicode letter number (“Nl”)
    UnicodeDigit ::
      Unicode decimal digit number (“Nd”)
    UnicodeCombiningMark ::
      Unicode nonspacing mark (“Mn”)
      Unicode spacing combining mark (“Mc”)
    UnicodeConnectorPunctuation ::
      Unicode connector punctuation (“Pc”)

Keywords

ReservedWord includes the following reserved words:

as
do
if
in
is

for
new
not
try
use
var

case
else
null
this
true
void
with

await
break
catch
class
const
false
super
throw
while
yield

delete
import
public
return
switch
typeof

default
extends
finally
package
private

continue
function
internal

interface
protected

implements

ContextKeyword is one of the following in certain syntactic contexts:

get
set
xml

each
enum
meta
type

Embed
final

native
static

decimal
generic

abstract
override

namespace

Punctuator

Punctuator includes one of the following:

::  @
.  ..  ...
(  )  [  ]  {  }
:  ;  ,
?  !  =
?.
<  <=
>  >=
==  ===
!=  !==
+  -  *  %  **
++  --
<<  >>  >>>
&  ^  |  ~
&&  ^^  ||  ??

The @ punctuator must not be followed by a single quote or a double quote character .

Punctuator includes CompoundAssignmentPunctuator. CompoundAssignmentPunctuator is one of the following:

+=  -=  *=  %=  **=
<<=  >>=  >>>=  &=  ^=  |=
&&=  ^^=  ||=
??=

Numeric Literal

NumericLiteral is similiar to NumericLiteral from the ECMA-262 third edition, with support for binary literals, underscore separators and certain suffixes:

0b1011
1_000

10f // float(10)
10m // decimal(10)
10n // bigint(10)

Syntax

    NumericLiteral ::
      DecimalLiteral DecimalLiteralSuffixopt [lookahead ∉ { IdentifierStart, DecimalDigit }]
      HexIntegerLiteral HexLiteralSuffixopt [lookahead ∉ { IdentifierStart, DecimalDigit }]
      BinIntegerLiteral BinLiteralSuffixopt [lookahead ∉ { IdentifierStart, DecimalDigit }]
    DecimalLiteralSuffix ::
      f
      F
      m
      M
      n
      N
    HexLiteralSuffix ::
      m
      M
      n
      N
    BinLiteralSuffix ::
      DecimalLiteralSuffix
    DecimalLiteral ::
      DecimalIntegerLiteral . UnderscoreDecimalDigitsopt
      ExponentPartopt
      . UnderscoreDecimalDigits ExponentPartopt
      DecimalIntegerLiteral ExponentPartopt
    DecimalIntegerLiteral ::
      0
      [lookahead = NonZeroDigit] UnderscoreDecimalDigitsopt
    DecimalDigits ::
      DecimalDigit{1,}
    UnderscoreDecimalDigits ::
      DecimalDigits UnderscoreDecimalDigits _ DecimalDigits
    DecimalDigit ::
      0-9
    NonZeroDigit ::
      1-9
    ExponentPart ::
      ExponentIndicator SignedInteger
    ExponentIndicator ::
      e
      E
    SignedInteger ::
      UnderscoreDecimalDigits
      + UnderscoreDecimalDigits
      - UnderscoreDecimalDigits
    HexIntegerLiteral ::
      0x UnderscoreHexDigits
      0X UnderscoreHexDigits
    HexDigit ::
      0-9
      A-F
      a-f
    UnderscoreHexDigits ::
      HexDigit{1,}
      UnderscoreDecimalDigits _ HexDigit{1,}
    BinIntegerLiteral ::
      0b UnderscoreBinDigits
      0B UnderscoreBinDigits
    BinDigit ::
      0
      1
    UnderscoreBinDigits ::
      BinDigit{1,}
      UnderscoreDecimalDigits _ BinDigit{1,}

Regular Expression Literal

RegularExpressionLiteral is similiar to RegularExpressionLiteral from the ECMA-262 third edition, with support for line breaks.

Syntax

    RegularExpressionLiteral ::
      / RegularExpressionBody / RegularExpressionFlags
    RegularExpressionBody ::
      RegularExpressionFirstChar RegularExpressionChars
    RegularExpressionChars ::
      «empty»
      RegularExpressionChars RegularExpressionChar
    RegularExpressionFirstChar ::
      SourceCharacter [but not * or \ or /]
      BackslashSequence
    RegularExpressionChar ::
      SourceCharacter [but not \ or /]
      BackslashSequence
    BackslashSequence ::
      \ SourceCharacter
    RegularExpressionFlags ::
      «empty»
      RegularExpressionFlags IdentifierPart

String Literal

StringLiteral is similiar to the StringLiteral symbol from the ECMA-262 third edition. The following additional features are included:

  • Scalar UnicodeEscapeSequence using the \u{...} form
  • Triple string literals
  • Raw string literals using the @ prefix

Triple string literals use either """ or ''' as delimiter and may span multiple lines. The contents of triple string literals are indentation-based, as can be observed in the following program:

const text = """
    foo
    bar
    """
text == "foo\nbar"

Triple string literals are processed as follows:

  • The first empty line is ignored.
  • The base indentation of a triple string literal is that of the last string line.

Both regular and triple string literals accept the @ prefix, designating raw string literals. Raw string literals contain no escape sequences.

const text = @"""
    x\y
    """

Escape sequences are described by the following table:

EscapeDescription
\’U+27 single-quote
\“U+22 double-quote
\\U+5C backslash character
\bU+08 backspace character
\fU+0C form feed character
\nU+0A line feed character
\rU+0D carriage return character
\tU+09 tab character
\vU+0B vertical tab character
\0U+00 character
\xHHContributes an Unicode code point value
\uHHHHContributes an Unicode code point value
\u{…}Contributes an Unicode code point value
\ followed by LineTerminatorContributes nothing

Syntax

    StringLiteral ::
      [lookahead ≠ """] " DoubleStringCharacter{0,} "
      [lookahead ≠ '''] ' SingleStringCharacter{0,} '
      """ TripleDoubleStringCharacter{0,} """
      ''' TripleSingleStringCharacter{0,} '''
      RawStringLiteral
    RawStringLiteral ::
      @ [lookahead ≠ """] " DoubleStringRawCharacter{0,} "
      @ [lookahead ≠ '''] ' SingleStringRawCharacter{0,} '
      @""" TripleDoubleStringRawCharacter{0,} """
      @''' TripleSingleStringRawCharacter{0,} '''
    DoubleStringCharacter ::
      SourceCharacter [but not double-quote " or backslash \ or LineTerminator]
      EscapeSequence
    SingleStringCharacter ::
      SourceCharacter [but not single-quote ' or backslash \ or LineTerminator]
      EscapeSequence
    DoubleStringRawCharacter ::
      SourceCharacter [but not double-quote " or LineTerminator]
    SingleStringRawCharacter ::
      SourceCharacter [but not single-quote ' or LineTerminator]
    TripleDoubleStringCharacter ::
      [lookahead ≠ """] SourceCharacter [but not backslash \ or LineTerminator]
      EscapeSequence
      LineTerminator
    TripleSingleStringCharacter ::
      [lookahead ≠ '''] SourceCharacter [but not backslash \ or LineTerminator]
      EscapeSequence
      LineTerminator
    TripleDoubleStringRawCharacter ::
      [lookahead ≠ """] SourceCharacter [but not LineTerminator]
      LineTerminator
    TripleSingleStringRawCharacter ::
      [lookahead ≠ '''] SourceCharacter [but not LineTerminator]
      LineTerminator

Escape Sequences

Syntax

    EscapeSequence ::
      \ CharacterEscapeSequence
      \0 [lookahead ∉ DecimalDigit]
      \ LineTerminator
      HexEscapeSequence
      UnicodeEscapeSequence
    CharacterEscapeSequence ::
      SingleEscapeCharacter
      NonEscapeCharacter
    SingleEscapeCharacter ::
      '
      "
      \
      b
      f
      n
      r
      t
      v
    NonEscapeCharacter ::
      SourceCharacter [but not EscapeCharacter or LineTerminator]
    EscapeCharacter ::
      SingleEscapeCharacter
      DecimalDigit
      x
      u
    HexEscapeSequence ::
      \x HexDigit HexDigit
    UnicodeEscapeSequence ::
      \u HexDigit{4}
      \u { HexDigit{1,} }

XML

This section defines nonterminals used in the lexical grammar as part of the XML capabilities of the ShockScript language.

If a XMLMarkup, XMLAttributeValue or XMLText contains a LineTerminator after parsed, it contributes such LineTerminator to the lexical scanner.

Syntax

    XMLMarkup ::
      XMLComment
      XMLCDATA
      XMLPI
    XMLWhitespaceCharacter ::
      U+20 space
      U+09 tab
      U+0D carriage return
      U+0A line feed
    XMLWhitespace ::
      XMLWhitespaceCharacter
      XMLWhitespace XMLWhitespaceCharacter
    XMLText ::
      SourceCharacters [but no embedded left-curly { or less-than <]
    XMLName ::
      XMLNameStart
      XMLName XMLNamePart
    XMLNameStart ::
      UnicodeLetter
      underscore _
      colon :
    XMLNamePart ::
      UnicodeLetter
      UnicodeDigit
      period .
      hyphen -
      underscore _
      colon :
    XMLComment ::
      <!-- XMLCommentCharactersopt -->
    XMLCommentCharacters ::
      SourceCharacters [but no embedded sequence -->]
    XMLCDATA ::
      <![CDATA[ XMLCDATACharacters ]]>
    XMLCDATACharacters ::
      SourceCharacters [but no embedded sequence ]]>]
    XMLPI ::
      <? XMLPICharactersopt ?>
    XMLPICharacters ::
      SourceCharacters [but no embedded sequence ?>]
    XMLAttributeValue ::
      " XMLDoubleStringCharactersopt "
      ' XMLSingleStringCharactersopt '
    XMLDoubleStringCharacters ::
      SourceCharacters [but no embedded double-quote "]
    XMLSingleStringCharacters ::
      SourceCharacters [but no embedded single-quote ']
    XMLTagPunctuator ::
      =
      &=
      >
      />

ShockScript: Expressions

The syntactic grammar for expressions declares the β superscript, which denotes a pair of definitions: allowIn and noIn.

Identifiers

Syntax

x
*
q::x
q::[k]     ;
(q)::x     ;
(q)::[k]   ;
@x
@[k]
@q::x
@q::[k]
@(q)::x
@(q)::[k]
    PropertyIdentifier :
      Identifier [when keywords are enabled]
      IdentifierName [when keywords are disabled]
      *
    Qualifier :
      PropertyIdentifier
      ReservedNamespace
    ReservedNamespace :
      public
      private
      protected
      internal
      meta
    SimpleQualifiedIdentifier :
      PropertyIdentifier
      Qualifier :: PropertyIdentifier
      Qualifier :: Brackets
    ExpressionQualifiedIdentifier :
      ParenExpression :: PropertyIdentifier
      ParenExpression :: Brackets
    NonAttributeQualifiedIdentifier :
      SimpleQualifiedIdentifier
      ExpressionQualifiedIdentifier
    QualifiedIdentifier :
      @ Brackets
      @ NonAttributeQualifiedIdentifier
      NonAttributeQualifiedIdentifier

Primary expressions

Syntax

    PrimaryExpression :
      NullLiteral
      BooleanLiteral
      NumericLiteral
      StringLiteral
      ThisLiteral
      AllLiteral
      RegularExpressionLiteral
      QualifiedIdentifier
      XMLLiteral
      FixedExpression
      ParenListExpression
      ArrayLiteral
      ObjectLiteral
      EmbedExpression

Null literal

Syntax

    NullLiteral :
      null

This literal

Syntax

    ThisLiteral :
      this

All literal

Syntax

**
    AllLiteral :
      **

Semantics

The all literal returns a value of a Flags enumeration filled with the all flags from that Flags enumeration. The context type must be a Flags enumeration.

Boolean literal

Syntax

    BooleanLiteral :
      true
      false

Numeric literal

Syntax

10
10.0
.0
10e5
1e+9
1e-9
0b1011
0xFF
10_000

String literal

Syntax

"shockscript"

Triple string literals span multiple lines and are indentation-aware:

(
    """
    shockscript, nicely beauty
    scripting.
    """
) == "shockscript, nicely beauty\nscripting."

Regular expression literal

Syntax

/(?:)/gi

XML literal

Syntax

    XMLLiteral :
      XMLMarkup
      XMLElement
      < > XMLElementContent </ >
    XMLElement :
      < XMLTagContent XMLWhitespaceopt />
      < XMLTagContent XMLWhitespaceopt > XMLElementContent </ XMLTagName XMLWhitespaceopt >
    XMLTagContent :
      XMLTagName XMLAttributes
    XMLTagName :
      { AssignmentExpressionallowIn }
      XMLName
    XMLAttributes :
      XMLWhitespace { AssignmentExpressionallowIn }
      XMLAttribute XMLAttributes
      «empty»
    XMLAttribute :
      XMLWhitespace XMLName [lookahead ≠ XMLWhitespaceopt = ] [lookahead ≠ XMLWhitespaceopt &= ]
      XMLWhitespace XMLName XMLWhitespaceopt = XMLWhitespaceopt { AssignmentExpressionallowIn }
      XMLWhitespace XMLName XMLWhitespaceopt &= XMLWhitespaceopt Block
      XMLWhitespace XMLName XMLWhitespaceopt = XMLWhitespaceopt XMLAttributeValue
    XMLElementContent :
      { AssignmentExpressionallowIn } XMLElementContent
      XMLMarkup XMLElementContent
      XMLText XMLElementContent
      XMLElement XMLElementContent
      «empty»

Array literal

Syntax

["shock", "script"]
    ArrayLiteral :
      [ Elisionopt ]
      [ ElementList ]
      [ ElementList , Elisionopt ]
    Elision :
      ,
      Elision ,
    ElementList :
      Elisionopt AssignmentExpressionallowIn
      Elisionopt LiteralRest
      ElementList , Elisionopt AssignmentExpressionallowIn
      ElementList , Elisionopt LiteralRest

Object literal

Syntax

    ObjectLiteral :
      { FieldList }
    FieldList :
      «empty»
      NonEmptyFieldList
      NonEmptyFieldList ,
    NonEmptyFieldList :
      LiteralField
      LiteralRest
      NonEmptyFieldList , LiteralField
      NonEmptyFieldList , LiteralRest
    LiteralRest :
      ... AssignmentExpressionallowIn
    LiteralField :
      FieldName : AssignmentExpressionallowIn
      NonAttributeQualifiedIdentifier
    FieldName :
      NonAttributeQualifiedIdentifier
      Brackets
      StringLiteral
      NumericLiteral

Embed expression

Syntax

Embed("flower.webp")

// UTF-8 text
Embed("data.txt", "text/plain")

// ByteArray
Embed("data.bin", "application/octet-stream")
    EmbedExpression :
      Embed ( MetadataEntryListopt )

Semantics

The default form of the embed expression specifying solely a path is implementation-defined, but always returns a string representing an URL.

The form that specifies a path followed by a "text/plain" option will embed the referenced file at the program’s static memory as an UTF-8 encoded text, returning the string data type.

The form that specifies a path followed by a "application/octet-stream" option will embed the referenced file at the program’s static memory as an octet stream, returning the ByteArray data type.

Fixed expression

Syntax

<?fixed={x}?>
    FixedExpression :
      <?fixed={ ListExpressionallowIn } ?>

Semantics

Deactivates lookup of dynamic properties (implemented through a meta-method such as meta::get(k) where k includes string or QName) in the enclosed expression.

Note: This is necessary in rare cases where, for example, a class implements its own dynamic properties which may collide with its prototype. In such cases, instance variables are not accessible unless the user uses the fixed expression <?fixed={exp}?>. In the case of this, the fixed expression is not necessary as in-scope lookup always uses fixed name lookup.

The fixed name lookup effect is propagated:

  • From parenthesized expressions to the inner expression
  • From dot operator to base expression
  • From brackets operator to base expression

Parenthesized expressions

Syntax

(x)
    ParenExpression :
      ( AssignmentExpressionallowIn )
    ParenListExpression :
      ParenExpression
      ( ListExpressionallowIn , AssignmentExpressionallowIn )

Super expression

Syntax

    SuperExpression :
      super
      super Arguments

Postfix expressions

Syntax

    PostfixExpression :
      FullPostfixExpression
      ShortNewExpression
    FullPostfixExpression :
      PrimaryExpression
      FullNewExpression
      FullPostfixExpression PropertyOperator
      SuperExpression PropertyOperator
      FullPostfixExpression NonNull
      FullPostfixExpression Arguments
      FullPostfixExpression TypeArguments
      FullPostfixExpression QueryOperator
      FullPostfixExpression [no line break] ++
      FullPostfixExpression [no line break] --
      FullPostfixExpression OptionalChaining
    NonNull :
      !
    OptionalChaining :
      ?. QualifiedIdentifier
      ?. Brackets
      ?. Arguments
      OptionalChaining PropertyOperator
      OptionalChaining NonNull
      OptionalChaining Arguments
      OptionalChaining TypeArguments
      OptionalChaining QueryOperator
      OptionalChaining OptionalChaining

Property accessors

Syntax

    PropertyOperator :
      . QualifiedIdentifier
      Brackets
    Brackets :
      [ ListExpressionallowIn ]

Query operators

Syntax

    QueryOperator :
      .. QualifiedIdentifier
      . ( ListExpressionallowIn )

Descendants operator

o..x

Semantics

The descendants operator o..x looks at o for the meta::descendants() method and returns the result of invoking that method with the given identifier key.

Filter operator

o.(*.name.startsWith("A"))

Semantics

The filter operator o.(...) looks at o for the meta::filter() method and creates a wildcard * binding inside the parenthesized expression that represents the element being tested. The parenthesized expression must return a boolean and represents an activation.

Call expressions

Syntax

    Arguments :
      ( )
      ( ListExpressionallowIn )
    ArgumentListallowIn :
      AssignmentExpressionβ
      ArgumentListβ , AssignmentExpressionβ

New expressions

Syntax

    FullNewExpression :
      new FullNewSubexpression Arguments
    FullNewSubexpression :
      PrimaryExpression
      FullNewExpression
      FullNewSubexpression PropertyOperator
      SuperExpression PropertyOperator
    ShortNewExpression :
      new ShortNewSubexpression
    ShortNewSubexpression :
      FullNewSubexpression
      ShortNewExpression

Unary expressions

Syntax

    UnaryExpression :
      PostfixExpression
      delete PostfixExpression
      void UnaryExpression
      await UnaryExpression
      typeof UnaryExpression
      ++ PostfixExpression
      -- PostfixExpression
      + UnaryExpression
      - UnaryExpression
      ~ UnaryExpression
      ! UnaryExpression

Binary expressions

The binary operators are left-associative, excluding the following cases:

  • The exponentiation operator ** is right-associative.

The short circuit operators (||, ^^) have the lowest precedence and the exponentiation operator (**) has the greatest precedence.

Exponentiation expressions

Syntax

    ExponentiationExpression :
      UnaryExpression
      UnaryExpression ** ExponentiationExpression

Multiplicative expressions

Syntax

    MultiplicativeExpression :
      ExponentiationExpression
      MultiplicativeExpression * ExponentiationExpression
      MultiplicativeExpression / ExponentiationExpression
      MultiplicativeExpression % ExponentiationExpression

Additive expressions

Syntax

    AdditiveExpression :
      MultiplicativeExpression
      AdditiveExpression + MultiplicativeExpression
      AdditiveExpression - MultiplicativeExpression

Shift expressions

Syntax

    ShiftExpression :
      AdditiveExpression
      ShiftExpression << AdditiveExpression
      ShiftExpression >> AdditiveExpression
      ShiftExpression >>> AdditiveExpression

Relational expressions

Syntax

    RelationalExpressionβ :
      ShiftExpression
      RelationalExpressionβ > ShiftExpression
      RelationalExpressionβ < ShiftExpression
      RelationalExpressionβ <= ShiftExpression
      RelationalExpressionβ >= ShiftExpression
      RelationalExpressionβ as [lookahead ≠ ! ] ShiftExpression
      RelationalExpressionβ as ! ShiftExpression
      RelationalExpressionβ is ShiftExpression
      RelationalExpressionβ is not ShiftExpression
      [β = allowIn] RelationalExpressionβ in ShiftExpression
      [β = allowIn] RelationalExpressionβ not in ShiftExpression

Equality expressions

Syntax

    EqualityExpressionβ :
      RelationalExpressionβ
      EqualityExpressionβ == RelationalExpressionβ
      EqualityExpressionβ != RelationalExpressionβ
      EqualityExpressionβ === RelationalExpressionβ
      EqualityExpressionβ !== RelationalExpressionβ

Bitwise expressions

Syntax

    BitwiseAndExpressionβ :
      EqualityExpressionβ
      BitwiseAndExpressionβ & EqualityExpressionβ
    BitwiseXorExpressionallowIn :
      BitwiseAndExpressionβ
      BitwiseXorExpressionβ ^ BitwiseAndExpressionβ
    BitwiseOrExpressionallowIn :
      BitwiseXorExpressionβ
      BitwiseOrExpressionβ | BitwiseXorExpressionβ

Logical expressions

Syntax

    LogicalAndExpressionβ :
      BitwiseOrExpressionβ
      LogicalAndExpressionβ && BitwiseOrExpressionβ
    LogicalXorExpressionallowIn :
      LogicalAndExpressionβ
      LogicalXorExpressionβ ^^ LogicalAndExpressionβ
    LogicalOrExpressionallowIn :
      LogicalXorExpressionβ
      LogicalOrExpressionβ || LogicalXorExpressionβ

Coalesce expression

Syntax

    CoalesceExpressionβ :
      CoalesceExpressionHeadβ ?? BitwiseOrExpressionβ
    CoalesceExpressionHeadβ :
      CoalesceExpressionβ
      BitwiseOrExpressionβ

Short circuit expressions

Syntax

    ShortCircuitExpressionβ :
      LogicalOrExpressionβ
      CoalesceExpressionβ

Conditional expressions

Syntax

    ConditionalExpressionβ :
      ShortCircuitExpressionβ
      ShortCircuitExpressionβ ? AssignmentExpressionβ : AssignmentExpressionβ

Non assignment expressions

Syntax

    NonAssignmentExpressionβ :
      ShortCircuitExpressionβ
      yield [no line break] NonAssignmentExpressionβ
      FunctionExpressionβ
      ShortCircuitExpressionβ ? NonAssignmentExpressionβ : NonAssignmentExpressionβ

Assignment expressions

Syntax

    AssignmentExpressionβ :
      ConditionalExpressionβ
      yield [no line break] AssignmentExpressionβ
      FunctionExpressionβ
      AssignmentLeftHandSide = AssignmentExpressionβ
      PostfixExpression CompoundAssignmentPunctuator AssignmentExpressionβ
      PostfixExpression /= AssignmentExpressionβ
    AssignmentLeftHandSide :
      ArrayPattern
      ObjectPattern
      PostfixExpression [but not ArrayLiteral or ObjectLiteral]

Function expression

Syntax

    FunctionExpressionβ :
      function FunctionCommonβ
      function IdentifierName FunctionCommonβ

List expressions

Syntax

    ListExpressionβ :
      AssignmentExpressionβ
      ListExpressionβ , AssignmentExpressionβ

ShockScript: Type expressions

Syntax

    TypeExpression :
      NonPrefixedTypeExpression
      ? NonPrefixedTypeExpression
    TypeExpressionList :
      TypeExpression
      TypeExpressionList , TypeExpression
    NonPrefixedTypeExpression :
      *
      void
      null
      [lookahead ≠ ( ] QualifiedIdentifier
      ( TypeExpression )
      ( TypeExpression , TypeExpressionList )
      TupleTypeExpression
      RecordTypeExpression
      FunctionTypeExpression
      NonPrefixedTypeExpression PropertyOperator
      NonPrefixedTypeExpression [lookahead = . ] QueryOperator
      NonPrefixedTypeExpression TypeArguments
      NonPrefixedTypeExpression NonNull
      NonPrefixedTypeExpression ?
    TupleTypeExpression :
      [ TypeExpression ]
      [ TypeExpression , TypeExpressionList ]
      [ TypeExpression , TypeExpressionList , ]
    RecordTypeExpression :
      {}
      { RecordTypeItemList }
    RecordTypeItemList :
      RecordTypeField
      RecordTypeField , RecordTypeItemList
      ... TypeExpression
    RecordTypeField :
      NonAttributeQualifiedIdentifier : TypeExpression
    FunctionTypeExpression :
      function ( ) : TypeExpression
      function ( FunctionTypeParameterList ) : TypeExpression
    FunctionTypeParameterList :
      FunctionTypeParameter
      FunctionTypeParameterList , FunctionTypeParameter
    FunctionTypeParameter :
      ... TypeExpressionopt
      TypeExpression [lookahead ≠ = ]
      TypeExpression =
    TypeArguments :
      . < TypeArgumentsList GenericGreaterThan
    GenericGreaterThan :
      >
      first greater-than > from the offending token
    TypeArgumentsList :
      TypeExpression
      TypeArgumentsList , TypeExpression

ShockScript: Patterns

Destructuring patterns may be used in a number of contexts, including variable bindings, try..catch clauses, switch type cases and assignment left-hand sides.

Where applicable, expressions are disambiguated into destructuring patterns, in which case any incompatible or illegal expression results in a syntax error; for example, an expression is disambiguated in a pattern in an assignment whose left-hand side starts with a bracket [ or a brace {.

Syntax

    Pattern :
      Identifier [when keywords are enabled]
      IdentifierName [when keywords are disabled]
      ArrayPattern
      ObjectPattern
    TypedPattern :
      Pattern [lookahead ≠ :]
      Pattern : TypeExpression

Array pattern

Syntax

    ArrayPattern :
      []
      [ ArrayPatternItemList ]
    ArrayPatternItemList :
      ,
      , ArrayPatternItemList
      Pattern
      Pattern , ArrayPatternItemList
      ... Pattern

Object pattern

Syntax

    ObjectPattern :
      {}
      { ObjectPatternFieldList }
    ObjectPatternFieldList :
      ObjectPatternField
      ObjectPatternField ,
      ObjectPatternField , ObjectPatternFieldList
    ObjectPatternField :
      FieldName : Pattern
      NonAttributeQualifiedIdentifier

ShockScript: Statements

The ω superscript used throughout the specification translates to one of { abbrev, noShortIf, full }.

Syntax

    Statementω :
      SuperStatement Semicolonω
      Block
      IfStatementω
      SwitchStatement
      DoStatement Semicolonω
      WhileStatementω
      ForStatementω
      WithStatementω
      ContinueStatement Semicolonω
      BreakStatement Semicolonω
      ReturnStatement Semicolonω
      ThrowStatement Semicolonω
      TryStatement
      ExpressionStatement Semicolonω
      DefaultXMLNamespaceStatement Semicolonω
      LabeledStatementω
    Substatementω :
      EmptyStatement
      Statementω
    Substatements :
      «empty»
      SubstatementsPrefix Substatementabbrev
    SubstatementsPrefix :
      «empty»
      SubstatementsPrefix Substatementfull
    Semicolonabbrev :
      ;
      VirtualSemicolon
      «empty»
    SemicolonnoShortIf :
      Semicolonabbrev
    Semicolonfull :
      ;
      VirtualSemicolon

Empty statement

Syntax

    EmptyStatement :
      ;

Expression statement

Syntax

    ExpressionStatement :
      [lookahead ∉ { function, { }] ListExpressionallowIn

Default XML namespace statement

The default xml namespace statement is used to specify the default E4X namespace used for lookups where the XML prefix is omitted, influencing the surrounding frame’s [[DefaultXMLNamespace]] internal property.

namespace ninja = "http://www.ninja.com/2007/build"

{
    default xml namespace = ninja
    // [[DefaultXMLNamespace]] = "http://www.ninja.com/2007/build"
}

// [[DefaultXMLNamespace]] = ""

Syntax

    DefaultXMLNamespaceStatement :
      default [no line break] xml [no line break] namespace = NonAssignmentExpressionallowIn

Super statement

Syntax

    SuperStatement :
      super Arguments

Block statement

Syntax

    Block :
      { Directives }

Labeled statement

Syntax

    LabeledStatementω :
      Identifier : Substatementω

If statement

Syntax

    IfStatementabbrev :
      if ParenListExpression Substatementabbrev
      if ParenListExpression SubstatementnoShortIf else Substatementabbrev
    IfStatementfull :
      if ParenListExpression Substatementfull
      if ParenListExpression SubstatementnoShortIf else Substatementfull
    IfStatementnoShortIf :
      if ParenListExpression SubstatementnoShortIf else SubstatementnoShortIf

Switch statements

The switch statement is similiar to that of Java. Unlike in Java, the switch statement does not include fallthroughs.

switch (v) {
    case 0:
    case 1:
        trace("zero or one");
    default:
        trace("other");
}

The switch type statement is used to match the type of a discriminant value.

switch type (v) {
    case (d : double) {
        // double
    }
    default {
        // no matching case
    }
}

Syntax

    SwitchStatement :
      switch ParenListExpression { CaseElementsabbrev }
      switch [no line break] type ParenListExpression { TypeCaseElements }
    CaseElementsω :
      «empty»
      CaseElementω
      CaseElementsfull CaseElementω
    CaseElementω :
      CaseLabel{1,} CaseDirectivesω
    CaseLabel :
      case ListExpressionallowIn :
      default :
    CaseDirectivesω :
      Directiveω
      CaseDirectivesfull Directiveω
    TypeCaseElements :
      «empty»
      TypeCaseElement
      TypeCaseElements TypeCaseElement
    TypeCaseElement :
      case ( TypedPattern ) Block
      default Block

Do statement

Syntax

    DoStatement :
      do Substatementabbrev while ParenListExpression

While statement

Syntax

    WhileStatementω :
      while ParenListExpression Substatementω

For statements

The for..in statement is used to iterate the keys of an object.

for (const key in map) {
    trace(key)
}

The for each statement is used to iterate the values of an object.

for each (const value in array) {
    trace(value)
}

Syntax

    ForStatementω :
      for ( ForInitializer ; ListExpressionallowInopt ; ListExpressionallowInopt ) Substatementω
      for ( ForInBinding in ListExpressionallowIn ) Substatementω
      for [no line break] each ( ForInBinding in ListExpressionallowIn ) Substatementω
    ForInitializer :
      «empty»
      ListExpressionnoIn
      VariableDefinitionnoIn
    ForInBinding :
      PostfixExpression
      VariableDefinitionKind VariableBindingnoIn

Continue statement

Syntax

    ContinueStatement :
      continue
      continue [no line break] Identifier

Break statement

Syntax

    BreakStatement :
      break
      break [no line break] Identifier

With statement

The with statement is used to declare a * binding to the statement’s scope. The * binding holds the value of the parenthesized expression.

with (o) {
    *.x += 10;
    *.y += 5;
}

Syntax

    WithStatementω :
      with ParenListExpression Substatementω

Return statement

Syntax

    ReturnStatement :
      return
      return [no line break] ListExpressionallowIn

Throw statement

Syntax

    ThrowStatement :
      throw [no line break] ListExpressionallowIn

Try statement

Syntax

    TryStatement :
      try Block CatchClauses
      try Block CatchClausesopt finally Block
    CatchClauses :
      CatchClause
      CatchClauses CatchClause
    CatchClause :
      catch ( TypedPattern ) Block

ShockScript: Directives

Syntax

    Directiveω :
      EmptyStatement
      Statementω
      ConfigurationConstantopt Attributesopt AnnotatableDirectiveω
      ConfigurationConstant Block
      ImportDirective Semicolonω
      UseNamespaceDirective Semicolonω
      UseDecimalDirective Semicolonω
      UseXMLDirective Semicolonω
    AnnotatableDirectiveω :
      NamespaceDefinition Semicolonω
      VariableDefinitionallowIn Semicolonω
      FunctionDefinition
      ClassDefinition
      EnumDefinition
      InterfaceDefinition
      TypeDefinition Semicolonω
    Directives :
      «empty»
      DirectivesPrefix Directiveabbrev
    DirectivesPrefix :
      «empty»
      DirectivesPrefix Directivefull
    ConfigurationConstant :
      Identifier :: IdentifierName

Attributes

Attributes are in the sequence of meta-data followed by modifiers. A parser shall disambiguate expressions into attributes as applicable.

Syntax

    Attributes :
      Attribute AttributeLineBreakRestriction
      AttributeCombination AttributeLineBreakRestriction
    AttributeCombination :
      Attribute AttributeLineBreakRestriction Attributes
    BlockAttributes :
      Metadata
      BlockAttributes Metadata
    AttributeLineBreakRestriction :
      no line break if the previous and offending tokens match an IdentifierName
    Attribute :
      Metadata
      UserAttribute
      ReservedNamespace
      final
      native
      static
      abstract
      override
      generic
    UserAttribute :
      Identifier
      UserAttribute . IdentifierName
    Metadata :
      MetadataPreRestriction [ MetadataForm ]]
      MetadataPreRestriction [ MetadataForm MetadataTrailingComma ]
    MetadataPreRestriction :
      if the Metadata is in the beginning of Attributes or if the Metadata appears before an IdentifierName in Attributes
    MetadataTrailingComma :
      comma , if the Metadata is the first occurrence in Attributes or BlockAttributes
    MetadataForm :
      MetadataName
      MetadataName ()
      MetadataName ( MetadataEntryList )
    MetadataName :
      Identifier
      Identifier :: IdentifierName
    MetadataEntryList :
      MetadataEntry
      MetadataEntryList , MetadataEntry
    MetadataEntry :
      MetadataName
      StringLiteral
      MetadataName = StringLiteral

Import directive

Syntax

    ImportDirective :
      import PackageName . *
      import PackageName . IdentifierName
      import Identifier = PackageName . *
      import Identifier = PackageName . IdentifierName

Use namespace directive

The use namespace directive is used to contribute a namespace to the open namespace list of the enclosing scope.

use namespace ns1

Syntax

    UseNamespaceDirective :
      use namespace ListExpressionallowIn

Use decimal directive

The use decimal directive is used to provide a DecimalContext instance at the surrounding frame for configuring the decimal data type.

const ctx = new DecimalContext("half_even") // round to even

{
    use decimal ctx
    x = a + b // "+" rounds to even if necessary
}

Syntax

    UseDecimalDirective :
      use decimal ListExpressionallowIn

Use XML directive

The use xml directive is used to provide a XMLContext object at the surrounding frame for configuring the XML/XMLList data types.

const ctx : XMLContext = {
    ignoreWhitespace: true,
}

{
    use xml ctx
    new XML("<a>  </a>")
}

Syntax

    UseXMLDirective :
      use xml ListExpressionallowIn

ShockScript: Definitions

Definitions use the public namespace by default.

Namespace definition

The namespace definition may be primarily used to define a namespace that may be used for protecting or versioning other definitions and XML data processing.

namespace ns1
namespace ns2 = "http://example.com/2015/product"

In addition, the namespace definition may also be used to define aliases to a package wildcard import, as in:

// com.inexistentninja.kunai.*
namespace kunai = "com.inexistentninja.kunai";

// an alias to the top-level package.
namespace ES4 = "http://www.sweaxizone.com/2015/shockscript/global";

Namespaces are allowed to nest within blocks regardless of scope. When inside a class block, contributes a static property.

Syntax

    NamespaceDefinition :
      namespace IdentifierName
      namespace IdentifierName = AssignmentExpressionallowIn

Semantics

A URI namespace contains at least a colon; namespaces assigned a string literal without a colon will result into an alias to a package wildcard import.

Variable definition

Syntax

    VariableDefinitionβ :
      VariableDefinitionKind VariableBindingListβ
    VariableDefinitionKind :
      var
      const
    VariableBindingListβ :
      VariableBindingβ
      VariableBindingListβ , VariableBindingβ
    VariableBindingβ :
      TypedDestructuring VariableInitializationβ
    VariableInitializationβ :
      «empty»
      = AssignmentExpressionβ

Function definition

Syntax

    FunctionDefinition :
      function FunctionName TypeParametersopt FunctionCommonallowIn

Function name

Syntax

    FunctionName :
      IdentifierName
      get [no line break] IdentifierName
      set [no line break] IdentifierName

FunctionName is used inside FunctionDefinition.

function f(): void {}
function get x(): double (impl.x)
function set x(v: double): void { impl.x = v }

TypeParameters may not appear in a function definition defining a getter, setter or constructor.

Function body

Syntax

function f():double 10

function f():void {
    // code
}
    FunctionCommonβ :
      FunctionSignature
      FunctionSignature [lookahead ∉ { { }] [inline, or in a greater indentation, or lookahead = **(**] AssignmentExpressionβ
      FunctionSignature Block

Function signature

Syntax

    FunctionSignature :
      ( Parameters ) ResultType
    ResultType :
      «empty»
      : TypeExpression

Parameter list

Syntax

    Parameters :
      «empty»
      NonemptyParameters
    NonemptyParameters :
      Parameter
      Parameter , NonemptyParameters
      RestParameter
    Parameter :
      TypedPattern
      TypedPattern = AssignmentExpressionallowIn
    RestParameter :
      ... TypedPatternopt

Class definition

Syntax

Nested classes are allowed; however, classes are only allowed in package blocks and top-level region. When nested in another class, contributes a static property.

    ClassDefinition :
      class IdentifierName TypeParametersopt Inheritance Block
    TypeParameters :
      . < TypeParameterList GenericGreaterThan
    TypeParameterList :
      TypeParameter
      TypeParameterList , TypeParameter
    TypeParameter :
      Identifier

Class inheritance

Syntax

    Inheritance :
      «empty»
      extends TypeExpression
      implements TypeExpressionList
      extends TypeExpression implements TypeExpressionList

Enum definition

Syntax

Nested enumerations are allowed; however, enumerations are only allowed in package blocks and top-level region. When inside a class block, contributes a static property.

    EnumDefinition :
      enum IdentifierName Block

Interface definition

Syntax

Interfaces may nest inside classes; outside of classes, interfaces are only allowed in package blocks and top-level region. When inside a class block, contributes a static property.

The interface block must only contain function definitions, which may only contain certain annotations: meta-data and the generic and meta attributes.

    InterfaceDefinition :
      interface IdentifierName TypeParametersopt ExtendsList Block

Interface inheritance

Syntax

    ExtendsList :
      «empty»
      extends TypeExpressionList

Type definition

A type definition is used to define an alias to an existing type.

Syntax

type M = Map.<double, double>

Type definitions are only allowed in package blocks and top-level region and inside class blocks. When inside a class block, contributes a static property.

    TypeDefinition :
      type IdentifierName TypeParametersopt = TypeExpression

Package definition

Syntax

    PackageDefinition :
      package Block
      package [no line break] PackageName Block
    ShortPackagePragma :
      package Semicolonfull
      package = PackageName Semicolonfull

A PackageDefinition may be used in a Program before any Directive that is not a PackageDefinition is used.

Package name

Syntax

    PackageName :
      Identifier
      PackageName . IdentifierName

Program definition

Syntax

    Program :
      ShortPackagePragma Directives
      NonPackageProgram
    NonPackageProgram :
      Directives
      PackageDefinition NonPackageProgram