Overview

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

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

eXtensible Markup Language

ShockScript incorporates eXtensible Markup Language (XML) expressions suitable for implementing user interfaces, and also includes capabilities for users interested on XML data processing.

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.

Documentation comments

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

Iteration capabilities

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, Number> {
    //
    public function keys() {
        for (var i = 0; i < 10; i++) {
            yield i.toString();
        }
    }
    //
    public function values() {
        for (var i = 0; i < 10; i++) {
            yield i;
        }
    }
}

Including media

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

Embed("thumb.webp")

Note: When returning an external URL, implementations such as Jet Engine use the app:// scheme to fetch a file in the application's installation directory. Implementations targetting HTML5 may choose to preload these files in a virtual file system.

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 application/text (String) or application/octet-stream (ByteArray), rather than obtaining a URL.

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

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 {
    //
    public 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:

public function emit.<E extends Event(this, object)>(e:E):Boolean {
    //
}

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:

public function on.<E extends Event(this, type)>(
    type:E.name, listener:function(E.type):void,
) : void {
    //
}

The third parameter was omitted for clarity.

Relation

This section compares ShockScript to other technologies and languages.

ActionScript 3

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

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

Include directive

The include directive is not included in ShockScript.

Default XML namespace

The default xml namespace = E4X statement is not included in ShockScript due to WebAssembly limitations.

Dynamic

The Object type is not dynamic per se, nor are there dynamic classes, nor are there legacy ECMAScript prototype objects. Only the * type is dynamic.

Matching: The str.match resulting object is slightly different.

Obtaining constructor: o.ReflectionProperties::constructor

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 shock_proxy::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 expressions 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(){}}/>
  • <?html={exp}?> alternative for tag content {exp}
<j:VGroup>
    <j:Label variant="heading">Welcome</j:Label>
    <j:Button click&={trace("clicked me");}>Click me</j:Button>
</j: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 structural object 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 the static="mime type" option.

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

MXML

The MXML language, as part of the Apache Flex framework, is used for describing UI components in an intuitive way. ShockScript uses XML expressions similiar to what is known as ECMAScript for XML (E4X) which implementations may use similiarly to MXML.

Note however that the ShockScript approach is more related to the React.js framework which is often combined with the JSX language extension for TypeScript.

The following demonstrates a basic UI component in Jet Engine:

package com.business.components {
    import j = jet.**;

    /** App bar */
    public function AppBar() {
        return (
            <j:HGroup>
                <j:Button click&={trace("clicked!")}>button 1</j:Button>
            </j:HGroup>
        );
    }
}

Event handlers

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

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 documentation comments. Classes continue using the Event meta-data, though without needing the @eventType tag.

React.js

ShockScript incorporates XML capabilities, and XML expressions 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 expression for Jet Engine:

import j = jet.**;

<j:HGroup>
    <j:Button click&={trace("clicked!")}>button 1</j:Button>
</j: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.business.components.AppBar/>

For brevity, you do either:

import bComps = com.business.components.**

<bComps:AppBar/>

or:

import com.business.components.*

<AppBar/>

Interpolation

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

<j:VGroup>
    {undefined}
    {null}
    {node}
    {nodeList}
    {plainText}
    <?html={someHTML}?>
</j:VGroup>

Conditional compilation

NAMESPACE::CONSTANT

NAMESPACE::CONSTANT {
    //
}

NAMESPACE::CONSTANT var x

Scope

This 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
      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

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

void

The void type consists of the undefined value.

null

The null type, not expressed in a type annotation, consists of the null value.

String

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 Code Point total.

Nullability

Even though it is a primitive type, the String type may be assigned null to indicate absence of value, as it is a reference type.

Note: null and "" are two different values. You may test for both through using String as a falsy condition.

Boolean

The Boolean type consists of the values false and true.

Number

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

BigInt

The BigInt type represents an arbitrary range integer.

float

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

decimal

The decimal type represents an IEEE 754 quadruple-precision floating point (binary128).

int

The int type represents a signed 32-bit integer.

uint

The uint type represents an unsigned 32-bit integer.

Array

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

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, Number>();

// x=10
map.x = 10;

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

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

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

Tuple

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]

Structural function

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

function(T1, T2=, ...[T3]):E

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]).

Structural object

Structural object types, { ... }, are simple property records, whose field order is sensitive. Those types are compiled into efficient structures.

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

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

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

Field omission

All fields are optional; however, if it is desired to default to undefined, a field such as x?:T is equivalent to x:(void,T).

Field order

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

Compatibility

Two structural object 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

...rest components may appear, where rest must be another structural object type; the resulting type may be a subtype of rest depending on whether properties do not collide and there is only one rest component.

// A
type A = { x:Number };
// B
type B = { y:Number, ...A };
// U
type U = { ...W, ...B };
// W
type W = { z:Number };

Union

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)

Default value

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

  1. If it contains void, then undefined.
  2. If it contains a type that accepts null, then null.
  3. Return the default value of the first member type.

Object

All types but { void, null, uint, int, float, Number, decimal, BigInt, Boolean } represent referenceable objects that may be null. 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.ReflectionProperties::constructor

This

The this type expression may be used to refer to the enclosing class. In subclasses, the this type expression will yield the subclass itself, not the original enclosing class.

class A {
    function m():this (this);
}

class B extends A {}

const obj = new B();
obj.m() // known as B

Packages

A package consists of a ascending domain name, a set of properties and two namespaces, public and internal.

A package com.business.enum is expressed as:

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:

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

Top-level package

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

package {
    //
}

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

Math
    // or
global::Math

Name shadowing

When a package or a specific item of a package is imported, it is possible to fully qualify a name in an expression using that package and the imported items, shadowing any other variables.

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

Documentation comment

A package definition may have a prefix documentation comment, allowing to document the package once.

/**
 * Enumerations used in the core runtime.
 */
package com.business.coreRT.enum {}

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 quantum package (excluding subpackages).

q::x

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

import com.business.quantum.*;

Package recursive import

A package recursive 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 quantum package and its subpackages in a recursive way.

q::x

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

import com.business.quantum.**;

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 are created implicitly by the language:

  • public
  • internal
  • protected
  • private
  • static protected

They are tied to a parent (such as a package, a class or a scope).

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.

Documentation comment

A class may be prefixed by a documentation 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 {}

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:Number;
    function A(x:Number) {
        this.x = x;
    }
}
class B extends A {
    var y:Number = 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:Number) {}
}
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 {
    public static const VALUE:Number = 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 teh 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.

Enumerations

Enumerations are special classes consisting of zero or more variants.

enum Variant {
    const VARIANT_ONE;
    const VARIANT_TWO = "variantTwo";
    const VARIANT_THREE = [2, "variantThree"];
}

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 = "variantOne";

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.

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 Number 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 ArrayInitializer 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 = a screaming snake case (ABC_DEF) to camel case (abcDef) conversion of orig.
  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()

public override function valueOf():T {
    //
}

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

toString()

public 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

shock_proxy::has()

shock_proxy 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()

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

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

without()

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

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

toggled()

public 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 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() : Number;
    function set x(value);
}

interface Ia extends I {}

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

Documentation comment

An interface may be prefixed by a documentation comment.

/** Comment */
interface I {}

Meta-data

An interface may have zero or more meta-data.

[M1]
[M2]
interface I {}

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 {
    shock_proxy 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 {}

Variables

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

var x = 0
const y = 10

Documentation comment

A documentation 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

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) {
    //
}

Documentation comment

A virtual variable derives documentation 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() {}
}

Documentation comment

A documentation 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.

function g():Iterator.<decimal> {
    yield 100.5;
}

If a method uses both yield and await, it is considered an iterator of Promise, therefore returning Iterator.<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() {
    await otherF();
}

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

Multi-methods

A method may be defined more than once with varying signatures, being considered a multi-method.

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

Overriding

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

override protected function m() {
    //
}

A getter must override a getter, and a setter must override a setter.

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.

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;

Documentation comment

An alias may be prefixed by a documentation comment.

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

Generics

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

Note: [Number], [float], [decimal], [int] and [uint] are specialized so they are represented in a memory efficient way.

class A.<T> {}
interface I.<T> {}
type Alias.<T> = (decimal, Complex.<T>);

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

Parameter constraints

Type parameters may have at most one constraint, such as extends.

function f.<T extends A>(o:T) {
}
function f.<T implements I>(o:T) {
}
function f.<E extends Event(A, type)>(type:E.name, value:E.type) {
}
function f.<E extends Event(A, object)>(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 {
    //
    public class EventTarget {
        //
        public function emit.<E extends Event(this, object)>(e:E):Boolean {
            //
        }
    }
}
  • Event(T, type) yields the name-type pair of an event.
  • Event(T, object) ensures event creation is correct by analyzing the new E(type, ...) expression.

Note: The Event(T, type) constraint's name field 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, package wildcard imports, and package recursive imports.

Scope variants

With scope

The with scope is created by the with statement, causing the wildcard * expression to resolve to the parenthesized object.

with (object.from.somewhere)
    *.x *= 10,
    *.y *= 3;

Added internal properties

NameDescription
[[Object]]Object used in the with statement.

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. The filter operator creates an activation specifying a [[FilterBase]] to create a wildcard * binding.

Added internal properties

NameDescription
[[This]]The this object.
[[Method]]Method.
[[FilterBase]]Base object of the filter operator.

Default 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 global alias may be used to access the top-level package.

global

The topmost scope defines a global property, which is an alias to the public namespace of the top-level 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)

Documentation comments

Documentation comments use the format /** */. Markdown notation is supported in documentation 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

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

Supported tags

@copy

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

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

@deprecated

@deprecated [Description]

@event

Indicates that a structural object type's property is an event. The tag does nothing but move the item to the Events section of the documentation, similiar to the Event meta-data used in class definitions.

@event

Note: This is

@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]

Proxies

Methods of the shock_proxy namespace may be implemented in a class or interface for overriding language behavior, known as proxies.

static shock_proxy::call()

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

shock_proxy static function call():T {}

shock_proxy::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()).

shock_proxy function get(key:K):V {
    //
}

shock_proxy::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()).

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

shock_proxy::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()).

shock_proxy function delete(key:K):Boolean {
    //
}

shock_proxy::has()

Overrides the behavior of the in operator.

shock_proxy function has(key:K):Boolean {
    //
}

shock_proxy::getAttribute()

Overrides the behavior of the .@k accessor.

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

shock_proxy::setAttribute()

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

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

shock_proxy::deleteAttribute()

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

shock_proxy function deleteAttribute(key:K):Boolean {
    //
}

shock_proxy::filter()

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

shock_proxy function filter(testFn:function(T):Boolean):E {
    //
}

shock_proxy::descendants()

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

shock_proxy 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 <?html={exp}?> markup.

Syntax

    InputElementDiv ::
      WhiteSpace
      LineTerminator
      Comment
      Identifier
      ReservedWord
      Punctuator
      /
      /=
      NumericLiteral
      StringLiteral
    InputElementRegExp ::
      WhiteSpace
      LineTerminator
      Comment
      Identifier
      ReservedWord
      Punctuator
      NumericLiteral
      StringLiteral
      RegularExpressionLiteral
      XMLMarkup
    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

each
enum
type

Embed
final

native
static

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 and underscore separators:

0b1011
1_000

Syntax

    NumericLiteral ::
      DecimalLiteral [lookahead ∉ { IdentifierStart, DecimalDigit }]
      HexIntegerLiteral [lookahead ∉ { IdentifierStart, DecimalDigit }]
      BinIntegerLiteral [lookahead ∉ { IdentifierStart, DecimalDigit }]
    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
      <?html=
    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 ::
      =
      &=
      >
      />