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 GUIs, and also includes capabilities for users interested on XML data processing.
For the Jet Engine, XML expressions create Spot nodes.
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.
ShockDoc
ShockScript supports documentation comments allowing for Markdown notation, special tags such as @throws
and media inclusion.
Embedding resources
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 Jet™ 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 text/plain
or application/text
(String
), or application/octet-stream
(ByteArray
), rather than obtaining a URL.
Embed("data.txt", static="text/plain")
Embed("data.bin", static="application/octet-stream")
XML capabilities
The ShockScript language includes XML capabilities.
XML expressions
XML expressions by default are used for creating implementation-defined objects; however, when the inference type is String
, XML
or XMLList
, XML expressions evaluate to one of these types.
package com.business.components {
//
public function AppBar() {
return (
<j:HGroup>
<j:Button click&={trace("clicked!")}>button 1</j:Button>
</j:HGroup>
);
}
}
package com.business.editor {
//
public function data(a:String):XML {
return (
<tag>{a}</tag>
);
}
}
const xn = XML(<tag/>);
Note: The official ShockScript Compiler supports the compiler option
xmleval
which decides the APIs used for evaluating default XML expressions in a project, however only a limited number of technologies are supported.
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:
<j:Button click&={trace("clicked!")}>Click me</j:Button>
If the event has no parameters, then the attribute above is equivalent to eventName={function(){statementList}}
.
Interpolation
<j:VGroup {params}>
{undefined}
{null}
{node}
{nodeList}
{plainText}
{number}
</j:VGroup>
Filtering
XML
and XMLList
implement the filter operator.
xnode.(*.@name == "Diantha")
Descendants
XML
and XMLList
implement the descendants operator.
xnode..tag
Jet XML capabilities
This section describes XML features specifically when applied as Jet+Spot nodes.
Native tags
Native tags belong to the implicit j
namespace, such as j:Button
. The j
namespace is not overridable.
DOM “data” attributes
data
attributes (like data-x
) set over native tags, such as j:Button
, contribute plain data attributes to the underlying DOM element.
Using a Spot reference, the attribute would be accessed as reference!.@["data-name"]
.
“key” attribute
The key
attribute is reserved for uniquely identifying interpolated collection items.
Linking cascading style sheets
<j: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 CustomComponent() {
return (
<j:Button>
<j:Style color="yellow">
<![CDATA[
:host { color: param(color) }
]]>
</j:Style>
click me
</j:Button>
);
}
}
If the style sheet is too large, it may be moved out of the ShockScript file; for instance:
// CustomComponent.sx
package com.fun.components {
//
public function CustomComponent() {
return (
<j:Button>
<j:Style
source="CustomComponent.css"
color="yellow"/>
click me
</j:Button>
);
}
}
/* CustomComponent.css */
:host {
color: param(color);
}
Linking style sheets in custom components
For a component to support <j:Style>
tags, it simply needs to support a stylesheet : [Spot::StyleSheet]
parameter.
package com.fun.components {
//
public function CustomComponent({
stylesheet
}: {
stylesheet? : [Spot::StyleSheet],
}) {
//
return (
<j:Button>
<j:Style extend={stylesheet}/>
click me
</j:Button>
);
}
}
Specifying inline styles
Use s:n={v}
attributes as a shortcut to style={{ ..., n: v }}
.
<j:Button s:background="orange">button1</j:Button>
Inline style values are converted to String
automatically.
Relation to other technologies
This section compares ShockScript to other technologies and languages.
MXML
The MXML language, as part of the Apache Flex framework, was used for describing UI components in an intuitive way. ShockScript uses XML expressions 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 Jet+Spot:
package com.business.components {
//
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="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 asclick&={trace("clicked!")}
rather than React.jsonClick={e=>{console.log("clicked!")}}
. Event parameters are conventionally given the@eventparam
tag in the ShockDoc comments. Classes continue using theEvent
meta-data, though without needing the@eventType
tag.
Rendering components
The Jet Engine's Spot feature allows programmers to implement UI components as functions that wrap around the built-in class-based components of Jet Engine. The component is rendered by evaluating the function initially and whenever a state changes.
Effects
The Spot::useEffect
hook may be used to detect state, parameter or derived changes as well as the component mount and unmount phases.
Spot::useEffect(function() {
// cleanup
return function() {
//
};
}, [dep1, ...depN]);
Spot::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.
Spot::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 is only assigned to the state the first time the component renders.
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];
References
In the top-level of a component, declare references by using the Reference
meta-data. References 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).
[Reference]
var button:Button? = null;
return (
<j:Button bind={button}>click me</j:Button>
);
Contexts
In the top-level of a component, reflect inherited contexts by using the Context
meta-data.
[Context(ThemeContext)]
var theme:Theme;
Capture safety
Unlike in React.js combined with TypeScript, states, references and context reflections are captured by reference from nested functions, guaranteeing the "outdated" value of, say, a state, is never captured, which facilitates development by requiring no additional reference declaration.
Styling
Unlike with React.js, there is built-in support for linking style sheets in a Spot component.
<j:Container>
<j:Style>
<![CDATA[
:host {
background: red;
}
]]>
</j:Style>
</j:Container>
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™:
<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 @eventparam
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 bc = com.business.components.**;
<bc:AppBar/>
or:
import com.business.components.*
<AppBar/>
Interpolation
Interpolation works similiarly to React.js, except for HTML.
<j:VGroup>
{undefined}
{null}
{node}
{nodeList}
{plainText}
{number}
</j:VGroup>
Interpolating attributes uses { object }
and not { ...object }
and must appear at most once at the end of all attributes:
<j:Button {params}>click me</j:Button>
States
Unlike React.js, in Jet+Spot there is no risk of accessing an outdated state's value, due to how states are constructed.
package com.business.components {
//
public function HelloWorld() {
[State]
var x:decimal = 0;
return (
<j:VGroup>
<j:Label>clicked {x} times</j:Label>
<j:Button click&={x++}>click me</j:Button>
</j: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 };
References
In Jet+Spot the concept of "refs" is more formally called references.
package com.business.components {
//
public function HelloWorld() {
[Reference]
var button:Button?;
//
Spot::useEffect(function() {
trace(button!);
}, []);
return (
<j:Button bind={button}>click me</j:Button>
);
}
}
Contexts
Context usage is represented as Spot::ContextReflection.<T>
objects, although they are used as natural Context
-annotated locals.
function ExampleComponent() {
//
[Context(ExampleContext)]
const example:Number;
return (
<></>
);
}
Effects
The popular "useEffect" hook requires the second argument, preventing mistakes. For listening to any changes, use "*"
.
Spot::useEffect(function() {
//
return function() {
// cleanup
};
}, [dep1, ...depN]);
Spot::useEffect(function() {
//
}, "*");
Styling
Unlike with React.js, there is built-in support for linking style sheets in a Spot component.
<j:Container>
<j:Style>
<![CDATA[
:host {
background: red;
}
]]>
</j:Style>
</j:Container>
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 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.Reflect::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 shock_proxy::has()
which is in general used for determining whether a collection contains a specific value; for Map
s 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(){}}/>
<j:VGroup>
<j:Label variant="heading">Welcome</j:Label>
<j:Button click&={trace("clicked me");}>Click me</j:Button>
</j:VGroup>
Note:
XML(<tag/>)
equalsvar _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
@eventparam
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 the static="mime type"
option.
Embed("data.txt", static="text/plain") // or "application/text"
Embed("data.bin", static="application/octet-stream")
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.
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 variable rather than a key-value pair; for that, the user may force fixture access using
<?fixed={object.q::n}?>
.
Variable shadowing
In ShockScript the following is valid in an activation:
var m:* = complexCentral.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 athis
binding: only the parameters. - Instance methods are bound.
Java
Package flexibility
ShockScript has an advantage over Java when it comes to packages: the user can import a package recursively and even alias it.
package com.business.product.core {
//
public class Chart {
//
}
}
package com.business.product.core.enum {
//
public enum ChartType {
const BAR;
const FLOW;
}
}
import pns = com.business.product.**;
//
const chart_type : pns::ChartType = "flow";
//
const chart = new pns::Chart(chart_type);
When doing so, the user must ensure that a name is not to collide with another name.
package com.company.product {
public const x = 0;
}
package com.company.product.omega {
public const x = 10, y = 10.5;
}
import pns = com.company.product.**;
pns::y // OK
pns::x // ERROR! name conflict
Access control namespaces
ShockScript allows the user to define properties tied to a namespace, which is useful for version control and protection.
package com.company.runner {
/** @private */
public namespace runner_internals;
}
package com.company.runner {
//
public class Helper {
/** @private */
runner_internals const cache : [Number] = [];
//
public function track() {
runner_internals::cache.push(0);
}
}
}
package com.company.runner.advanced {
import com.company.runner.*;
//
public function f(helper:Helper) {
helper.runner_internals::cache.push(10);
}
}
Namespaces additionally apply to record types.
package com.business.product {
/**
* Flexible version control namespace.
*/
public namespace Flexible = "http://business.com/product/flexible";
}
package com.business.product {
/**
* Judgement version control namespace.
*/
public namespace Judgement = "http://business.com/product/judgement";
}
package com.business.product {
/**
* Pair.
*/
public type Pair = {
Flexible::strength : [decimal],
Judgement::strength : [decimal],
};
}
Meta-data
Meta-data are bracketed, arbitrary entries of textual key-value pairs that may be attached to definitions. Meta-data are not unique and may appear more than once, as well as key-value pairs.
[M1]
class A {}
[M1(x="y", z="w")]
class A {}
[M1(y)]
class A {}
Keyless entries are a single identifier or a string literal not accompanied by a key.
Reserved meta-data
Certain meta-data are reserved in some contexts, such as Event
.
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 be attached multiple constraints.
[Limit(T, subtypeOf="A")]
function f.<T>(o:T) {
//
}
[Limit(E, eventOf="A", match="type")]
function f.<E>(type:E.name, value:E.type) {
//
}
[Limit(E, eventOf="A", match="object")]
function f.<E>(value:E) {
//
}
“eventOf” constraints
eventOf
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.
eventOf
constraints are allowed to take this
as the base type, reflecting the current class's events:
package com.business.coreRT.events {
//
public class EventTarget {
//
[Limit(E, eventOf="this", match="object")]
public function emit.<E>(e:E):Boolean {
//
}
}
}
match="type"
yields the name-type pair of an event. The.type
property of the pair relies on previous introduction of the respective.name
somewhere.match="object"
ensures event creation is correct by analyzing thenew E(type, ...)
expression.
Note: The
match="type"
constraint contributes aname
field that yields theString
type, but its purpose is for auto completion in integrated development environments.
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:
[Limit(E, eventOf="this", match="object")]
public function emit.<E>(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:
[Limit(E, eventOf="this", match="type")]
public 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
Clonage
The [object Object].clone()
implementation clones tuples, arrays and maps structurally; for class
instances, the default implementation requires an optional constructor. You may override clone
for a custom clonage implementation.
object.clone()
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():Iterator.<String> {
for (var i = 0; i < 10; i++) {
yield i.toString();
}
}
//
public function values():Iterator.<Number> {
for (var i = 0; i < 10; i++) {
yield i;
}
}
}
Environment variables
Environment variables may be read from the project's .env
file using the shock_env::VAR_NAME
expression:
shock_env::SECRET
Type matching
“is” operator
v is T
“switch type” statement
switch type (v) {
case (d:Date) {
trace("date");
}
case ([x, y] : [Number, Number]) {
trace("tuple of double");
}
default {
trace("any other");
}
}
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 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 usingString
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 a 128 bit decimal number.
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 aMap
equals aMap
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.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(...) : 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]
).
Record
Record 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,
};
Note: Record types do not match with types structurally. 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.
package com.business.product {
/**
* Flexible version control namespace.
*/
public namespace Flexible = "http://business.com/product/flexible";
}
package com.business.product {
/**
* Judgement version control namespace.
*/
public namespace Judgement = "http://business.com/product/judgement";
}
package com.business.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.
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:Number };
// B < A
type B = { y:Number, ...A };
Union
The structural union type, written (m1, m2, ...mN)
, consists of two or more non-union member types containing all possible values of the member types.
(decimal, String)
Restrictions
- Unions never contain the
*
type. - Unions contain two or more members.
Default value
The default value of an union type is determined as follows:
- If it contains
void
, thenundefined
. - If it contains
null
, thennull
. - No default value.
Nullability
The following shorthands are available for nullability:
T?
or?T
is equivalent to(null, T)
.T!
removesnull
and/orvoid
fromT
.
Object
All types but { void
, null
, uint
, int
, float
, Number
, decimal
, BigInt
, Boolean
} represent referenceable objects, which are nullable by default. 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.Reflect::class()
This
The this
type expression may be used to match the this
literal's type, which changes across subclasses.
class A {
function chainable():this (this);
}
class B extends A {}
const obj = new B();
obj.chainable() // known as B
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 shock_proxy::call()
method.
T(v) // failure throws a TypeError
v as T // failure returns a default value
Constant coercions
Constant coercions occur implicitly both at compile-time and runtime, converting a constant into another constant.
Kind | Result |
---|---|
undefined to flag enumeration | Interned instance whose value is zero (0). |
null to flag enumeration | Interned instance whose value is zero (0). |
undefined to T containing both undefined and null | undefined |
undefined to T containing undefined and no null | undefined |
undefined to T containing null and no undefined | null |
null to T containing undefined but not null | undefined |
null to T containing null but not undefined | null |
null to T containing both undefined or null | null |
Numeric constant to * or Object | Equivalent constant of the target type. |
String constant to * or Object or union containing String | Equivalent constant of the target type. |
Boolean constant to * or Object or union containing Boolean | Equivalent constant of the target type. |
Namespace constant to * or Object or union containing Namespace | Equivalent constant of the target type. |
Type constant to * or Object or union containing Class | Equivalent constant of the target type. |
Numeric constant to another compatible numeric type | Numeric constant with value coerced to target type. |
Numeric constant to union containing at least one compatible numeric type | Numeric 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 float | NaN |
-Infinity to float | -Infinity |
+Infinity to float | +Infinity |
Implicit coercion
Implicit coercions occur implicitly both at compile-time and runtime, after trying a constant coercion.
Kind | Result |
---|---|
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 inheritObject
.
Explicit conversions
Explicit conversions occur when resolving v as T
or T(v)
, after trying an implicit coercion.
Kind | Result |
---|---|
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] type | An array filtering out incompatible elements. |
String to enumeration | Identification 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 String | For undefined , returns "undefined" ; for null , returns "null" ; for other types, invokes toString() . |
To Boolean | Evaluates truthy value. |
To Number | Forced conversion to double-precision floating point. |
To float | Forced conversion to single-precision floating point. |
To decimal | Forced conversion to 128-bit decimal number. |
To int | Forced conversion to 32-bit signed integer. |
To uint | Forced conversion to 32-bit unsigned uninteger. |
To BigInt | Forced conversion to an arbitrary range integer. |
Record type into equivalent record type of non-uniform field order | |
From type parameter |
Note:
interface
types inheritObject
.
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
Number
: undefined.
PropertyLookup()
PropertyLookup(base, openNsSet, qual, key as LookupKey, followedByCall as Boolean
, forceFixture as Boolean
) takes the following steps in order, where forceFixture 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 localName is undefined or (qual is specified and qual is not a namespace nor a NamespaceConstant)
- 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 forceFixture == false) and baseType defines an instance method
shock_proxy::get
(possibly a multi method)- Let foundRegularProperty = false
- For each
shock_proxy::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, proxy method, qual as a
Namespace
object, key.Value coerced to (String
or defer))
- Return KeyValuePairReferenceValue(base, proxy method, qual as a
- Continue loop
- If K ==
- If key.Value is a (
String
or defer) value and (K == (QName
or defer) or (K ?union does not containString
and K union containsQName
))- Return KeyValuePairReferenceValue(base, proxy method, undefined, key.Value)
- If K ==
*
or K == (Object
or defer) or K == (String
or defer) or K == (QName
or defer) or (K ?union containsString
or K union containsQName
)- foundRegularProperty = true
- If (static type of key.Value or defer) fails on implicit coercion to K
- Continue loop
- Return KeyValuePairReferenceValue(base, proxy method, undefined, key.Value implicitly coerced to K)
- If qual != undefined
- 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, forceFixture)
- If doubleKey != undefined and baseType is a tuple
- If baseType ==
*
- Return DynamicReferenceValue(base, qual, key.Value, followedByCall, forceFixture)
- 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)
- For each descending class in baseType hierarchy
- 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
- For each descending itrfc in baseType hierarchy
- 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 localName is undefined or (qual is specified and qual is not a namespace nor a NamespaceConstant)
- If base is a type parameter with an
eventOf=, match="type"
constraint- If localName is undefined or (qual is specified)
- Return undefined.
- If localName = name
- Return EventNameType(type parameter).Wrap()
- If localName = type
- Return EventTypeType(previously introduced EventNameType).Wrap().
- Return undefined.
- If localName is undefined or (qual is specified)
- 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 is unresolved.
InScopeLookup()
InScopeLookup(scope, qual, key as LookupKey, followedByCall as Boolean
, forceFixture as Boolean
) takes the following steps in order:
Note: Content lacking.
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 asskia
rather thancom.google.skia
). Theme
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) );
ShockDoc comment
A package
definition may have a prefix ShockDoc 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.**;
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 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.
ShockDoc comment
A class may be prefixed by a ShockDoc 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: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:
- Let r = empty string
- If the initializer does not contain a string literal
- Let orig = binding identifier name
- r = a screaming snake case (
ABC_DEF
) to camel case (abcDef
) conversion of orig.
- Else
- r = the value of the string literal at the initializer.
- If r is already used by another variant's name
- Throw a verify error
- Return r
Variant value
The variant value as declared by the const
is determined as follows:
- If the enumeration is a flag enumeration 2. Return DecideFlagValue()
- Return DecideValue()
DecideValue()
- Let r = zero
- If the initializer does not contain a numeric literal
- If there is no previous variant, return 0.
- Let k = previous variant's value
- r = k + 1
- Else
- r = the value of the numeric literal at the initializer.
- If r is already used by another variant's value
- Throw a verify error
- Return r
DecideFlagValue()
- Let r = zero
- If the initializer does not contain a numeric literal
- If there is no previous variant, return 1.
- Let k = previous variant's value
- r = k * 2
- Else
- r = the value of the numeric literal at the initializer.
- If r is not one or a power of two
- Throw a verify error
- If r is already used by another variant's value
- Throw a verify error
- 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 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() : Number;
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.
ShockDoc comment
An interface may be prefixed by a ShockDoc 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 {
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
ShockDoc comment
A ShockDoc 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:* = complexCentral.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) {
//
}
ShockDoc comment
A virtual variable derives ShockDoc 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() {}
}
ShockDoc comment
A ShockDoc 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():Generator.<decimal> {
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() {
await otherF();
}
If a method uses both yield
and await
, it is considered an iterator of Promise
, therefore returning Generator.<Promise.<T>>
.
Multi-methods
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.
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() {
//
}
Remarks
- A getter must override a getter, and a setter must override a setter.
- For a multi method, the override shall match a specific signature.
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.
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():this {
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;
ShockDoc comment
An alias may be prefixed by a ShockDoc comment.
/** Comment */
type Params = {
x : decimal,
};
Lexical scopes
Internal properties
Name | Description |
---|---|
[[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
Name | Description |
---|---|
[[Object]] | Object used in the with statement. |
Class scope
Added internal properties
Name | Description |
---|---|
[[Class]] | Class object. |
Enum scope
Added internal properties
Name | Description |
---|---|
[[Class]] | Class object. |
Interface scope
Added internal properties
Name | Description |
---|---|
[[Interface]] | The interface. |
Package scope
Added internal properties
Name | Description |
---|---|
[[Package]] | Package. |
Activation
Method bodies create an activation as scope.
Added internal properties
Name | Description |
---|---|
[[This]] | The this object. |
[[Method]] | Method. |
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 a package wildcard import of the top-level package.
Intl
The topmost scope defines a Intl
property, which is an alias to shock.intl.**
.
Temporal
The topmost scope defines a Temporal
property, which is an alias to shock.temporal.**
.
Provided by Jet™
The following names and imports are provided implicitly when targetting Jet™.
jet.**
The topmost scope imports jet.**
, so that the Jet™ APIs are made available globally.
Spot
The topmost scope defines a Spot
property, which is an alias to a package recursive import of jetx.spot.**
, used for creating UI components using Jet+Spot.
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)
ShockDoc
ShockDoc are documentation comments that use the format /** */
. Markdown notation is supported in ShockDoc 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
ShockDoc comments may refer to relative images through the Markdown notation 
.
Supported tags
@copy
Copies ShockDoc 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...
@eventparam
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.
@eventparam
@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
orQName
key type (including base types*
andObject
) will override all names (like.x
), except when calling a method (like.m()
).If the user needs to access an instance variable in such a case it is recommended to use a force fixture access as in
<?fixed={object.q::n}?>
shock_proxy function get(key:K):V {
//
}
shock_proxy::set()
Note: Overriding the property accessor with a possibly
String
orQName
key type (including base types*
andObject
) 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
orQName
key type (including base types*
andObject
) 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:
Token | Input goal |
---|---|
/(?:)/ | InputElementRegExp |
; | InputElementDiv |
a | InputElementRegExp |
/ | InputElementDiv |
b | InputElementRegExp |
; | InputElementDiv |
< | InputElementRegExp |
a | InputElementXMLTag |
> | InputElementXMLTag |
Text | InputElementXMLContent |
</ | InputElementXMLContent |
a | InputElementXMLTag |
> | 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
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:
Escape | Description |
---|---|
\' | U+27 single-quote |
\" | U+22 double-quote |
\\ | U+5C backslash character |
\b | U+08 backspace character |
\f | U+0C form feed character |
\n | U+0A line feed character |
\r | U+0D carriage return character |
\t | U+09 tab character |
\v | U+0B vertical tab character |
\0 | U+00 character |
\xHH | Contributes an Unicode code point value |
\uHHHH | Contributes an Unicode code point value |
\u{...} | Contributes an Unicode code point value |
\ followed by LineTerminator | Contributes 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 ::
-
=
&=
>
/>