Language Overview

VioletScript is a robust strongly-typed scripting language. Its syntax is similiar to JavaScript, but it has different semantics.

VioletScript is designed to have a performance similiar to Java and C#, with a very pleasing syntax familiar for JavaScript developers.

Quick Tour

Why

VioletScript is a faster version of JavaScript efficient as C# or Java.

Status

  • Type checker is done.
  • Codegen is not yet done. It'll target either WebAssembly or C++.

Basics

trace('Hi!')

var x: Number = 0

var y = 0 // y: Number

const z = 0

var w: * = undefined // '*' is the any type

function f1(): Number {
    return Infinity
}

function f2(): Number (
    Infinity
);

// you can shadow variables
function shadowing(): void {
    'use shadowing'
    const x = false
    const x = ''
}

function typeSystem(): void {
    const array: [Number] = []

    const tuple: [String, Boolean] = ['', false]

    const union: Number | undefined | null = null

    const nullable: null | Boolean

    const nullable: Boolean? // equivalent to previous constant

    // records are immutable structures
    const record: {x: Number, y: Number} = {x: 10, y: 10}

    // the following have equivalent types
    const record: {x?: Number} = {};
    const record: {x: undefined | Number} = {};

    // as you saw above, a record field is optional
    // when it can contain undefined.

    // function types
    const fn: (...arguments: [Number]) => void = () => {}
}

type D = Decimal;

function nullability(): void {
    // asserts that `o` is non-null and non-undefined;
    // returns a type without undefined and null.
    o!

    // optional chaining
    o?.x.y?.z?.[i]?.()
    a?.[i]
    f.()
}

Enums

An enum is an object...

  • represented in memory as a primitive number;
  • whose possible variants are associated with...
    • an unique user friendly string and
    • an unique number;
  • that can define custom properties;
  • that, where expected, a variant can omit its name through a string literal;
enum E {
    X;
    Y = 'y';
    Z = ['z', 10];

    function f(): void {
        trace(this.valueOf());
    }
}

const e: E = 'x';
e.f(); // 0
e.valueOf() // 0
e.toString() // 'x'

const e: E = 0 // ERROR! number must be
// explicitly converted

const e: E = 'inexistent'; // ERROR! doesn't exist

Note that the default user friendly string is a conversion from SCREAMING_SNAKE_CASE (constant name) to screamingSnakeCase.

Flags

Flags enums have many methods by default, such as toggle() and filter().

[Flags]
enum F { X; Y; Z; }

const f: F = ['x'];
const f: F = {x: true};
const f: F = f.toggle('x');
trace('x' in f);
trace('x' not in f);

Classes

// generic class
class C.<T> {
    var x: T

    // constructor
    function C(x: T) {
        this.x = x
    }

    // the type `T!` can be used to remove
    // any undefined and null from the parameter type.
}

Packages and Namespaces

// shares items to all scripts.
package q.f {
    const someString = 'violetscript'
}

import q.f.*;
import q.f.someString;

// block brackets are optional
package q.b;

// a namespace acts as a simple container for
// other items, similiar to a singleton.
namespace Q {
    class C {
    }

    // define a function using the reserved word 'for'.
    // use '#' to escape a reserved word.
    function #for(x: Number): Number (
        x + 10
    );
}

Q.for(10);

Visibility

Everything is public by default, so you don't need to use visibility modifiers:

  • public
  • private
  • protected
  • internal

They're also verbose, so you may prefer using a _ prefix.

Type Testing

function typeTesting(): void {
    if (animal is falcon: Falcon && falcon.name.startsWith('F')) {
        // falcon: Falcon
    }

    switch type (object) {
        case (long: Long) {
            // Long
        }
        default {
            // anything else
        }
    }
}

Destructuring

const {} = o
const [a] = o

// use '!' to assert non-null
// and non-undefined
const [a!] = a
const {x!: {}} = o

({} = o); // destructuring assignment

Include

Use include to:

  • Fragment a class into multiple files.
  • Specifying the initialization order of static properties by including scripts in the wished order.
include './anotherScript';

Properties

o.x and o['x'] are not the same thing as in ECMAScript. o.x and Reflect.get(o, 'x') are equivalent.

Equality

Auto-boxed types do not lose equality.

var x: Object = 0;
var y: Object = 0;
assert(x == y);

Decorators

[D]
class C {
}

Note that six lexical decorator names are reserved in different contexts.

  • Flags is reserved for enum
  • Value is reserved for class
  • DontInit is reserved for clas
  • Metadata is always reserved
  • Allow is always reserved
  • Warn is always reserved
  • FFI is always reserved

You can still use these decorator names if they're in a namespace or package:

[q.b.Metadata]
class C {
}

Markup

A class must implement IMarkupContainer for supporting children in markup.

// equivalent
var o = new C
o.x = Infinity
var o = <C x={Infinity}/>

Markup syntax is not based on XML and cannot contain text directly. There are different ways of adding text:

<Container><Text text='Some text'/></Container>
// if child type can be string
<Container>{['Some text', 'Another text']}</Container>

Primitive Types

  • Boolean
  • String
  • Number
  • Decimal
  • Byte
  • Short
  • Int
  • Long
  • BigInt

Code Safety

  • Assignments are only allowed in proper contexts, otherwise that would lead to productivity issues.
  • You will use the ! operator very often, including in destructuring, for asserting that a base is not undefined and null.

Reflection

Constructing Types Dynamically

Use Reflect.construct() to construct a type dynamically, rather than new:

const c = SomeClass;
const o = Reflect.construct(c, [firstArgument]);

Type Inspection

Use Reflect.describeType() to get a type meta-object. It returns one of:

  • null
  • AnyType
  • UndefinedType
  • NullType
  • ArrayType
  • ClassType
  • EnumType
  • InterfaceType
  • TypeWithArguments
  • UnionType
  • TupleType
  • RecordType
  • FunctionType
  • TypeParameter

Frameworks

Unreal Engine

No plans yet.

import com.ue.*;

Language User Guide

This section lists all language features of VioletScript, avoiding grammar and implementation details.

Types

Nullability

The following line aliases the dynamic type (*):

type UndefinedOrNullOrObject = *;

The following line aliases the Object type:

type ObjectNotUndefinedNorNull = Object;

The following line escapes o from either null or undefined:

o!

The following line escapes T from union with null and undefined:

type NonNullT = T!;

Unifying type with null

The following line:

type NullableT = T?;

is equivalent to:

type NullableT = null | T;

It can also be wrote with a ? prefix:

type NullableT = ?T;

Unifying type with undefined

type UndefinedOrT = undefined | T;

Proxies

VioletScript supports proxies to alter the behavior of certain operations. Currently, only class and enum can define proxies; interface cannot define proxies.

Proxies cannot be overriden by subclasses.

Conversion Proxies

These proxies do not enable use of the literal this.

class C {
    proxy function convertImplicit(a: T): C? {
        // implicit conversion from A to C
    }

    proxy function convertExplicit(a: T): C? {
        // explicit conversion from A to C
    }
}

Unary Operator Proxies

These proxies do not enable use of the literal this.

class C {
    // +o;
    proxy function positive(a: C): R ...;

    // -o;
    proxy function negate(a: C): R ...;

    // ~o;
    proxy function bitNot(a: C): R ...;
}

Binary Operator Proxies

These proxies do not enable use of the literal this.

class C {
    // a === b;
    proxy function equals(a: C, b: C): Boolean ...;

    // a !== b;
    proxy function notEquals(a: C, b: C): Boolean ...;

    // a < b;
    proxy function lt(a: C, b: C): Boolean ...;

    // a > b;
    proxy function gt(a: C, b: C): Boolean ...;

    // a <= b;
    proxy function le(a: C, b: C): Boolean ...;

    // a >= b;
    proxy function ge(a: C, b: C): Boolean ...;

    // a + b;
    proxy function add(a: C, b: B): R ...;

    // a - b;
    proxy function subtract(a: C, b: B): R ...;

    // a * b;
    proxy function multiply(a: C, b: B): R ...;

    // a / b;
    proxy function divide(a: C, b: B): R ...;

    // a % b;
    proxy function remainder(a: C, b: B): R ...;

    // a ** b;
    proxy function pow(a: C, b: B): R ...;

    // a & b;
    proxy function bitAnd(a: C, b: B): R ...;

    // a ^ b;
    proxy function bitXor(a: C, b: B): R ...;

    // a | b;
    proxy function bitOr(a: C, b: B): R ...;

    // a << b;
    proxy function leftShift(a: C, b: B): R ...;

    // a >> b;
    proxy function rightShift(a: C, b: B): R ...;

    // a >>> b;
    proxy function unsignedRightShift(a: C, b: B): R ...;
}

Index Proxies

These proxies enable use of the literal this.

class C {
    // o[i];
    proxy function getIndex(i: I): V (
        ...
    );

    // o[i] = v;
    proxy function setIndex(i: I, v: V): void {
        ...
    }

    // delete o[i];
    proxy function deleteIndex(i: I): Boolean {
    	...
    }

    // v in o;
    proxy function has(v: V): Boolean (
    	...
    );
}

Iteration Proxies

These proxies enable use of the literal this.

class C {
    // for..in
    proxy function iterateKeys(): Iterator.<K> {
        // yield
    }

    // for each
    proxy function iterateValues(): Iterator.<V> {
        // yield
    }
}

Type Conversions

Conversion Syntax

// optional conversion
v as T
v as? T

// forced conversion
v as! T
Enum(v)

// conversion constructors
String(v)
Number(n)

Any Type

The dynamic type (*) includes values of all types.

Undefined Type

The undefined type includes the value undefined.

Null Type

The null type includes the null value.

Boolean Type

The Boolean type includes the values false and true.

Number Type

There are different numeric types:

type DoublePrecisionFloatingPoint = Number;

type QuadruplePrecisionFloatingPoint = Decimal;

type Unsigned8BitInteger = Byte;

type Signed16BitInteger = Short;

type Signed32BitInteger = Int;

type Signed64BitInteger = Long;

type ArbitraryPrecisionInteger = BigInt;

String Type

The String type is UTF-16 encoded for compatibility with the ECMA-262 String type.

Code Points

You can easily iterate code points over a string with CodePointIterator:

var iterator = 'v'.codePoints();
iterator.next(); // 0x76
iterator.next(); // 0 (no character)
iterator.hasRemaining; // false

Enumeration Types

Enumeration types define variants with an unique number and an unique string.

The following example program defines an enumeration with four variants:

enum E {
	VARIANT_A;
	VARIANT_B = 'customizedName';
	VARIANT_C = 64;
	VARIANT_D = ['anotherCustomizedName', 10];
}

// inference: 'variantA' converts to E.VARIANT_A
var e: E = 'variantA';

E.VARIANT_A.valueOf() == 0;
E.VARIANT_A.toString() == 'variantA';

E.VARIANT_B.valueOf() == 1;
E.VARIANT_B.toString() == 'customizedName';

E.VARIANT_C.valueOf() == 64;
E.VARIANT_C.toString() == 'variantC';

E.VARIANT_D.valueOf() == 10;
E.VARIANT_D.toString() == 'anotherCustomizedName';

Custom Properties

The block of an enum definition can contain anything, as the following program demonstrates:

enum E {
	function f(): void {
	}
}

Type Inference

Wherever a value of an enum is expected, a string literal can be used. In addition, for flags enumerations, an object initializer, array initializer or undefined can also be used.

var e: E = E.VARIANT_A
var e: E = 'variantA'

// flags inference
var f: F = undefined
var f: F = {}
var f: F = []

Flags

Flags enumerations, prefixed with the special Flags decorator, define combinatory variants. A variant number is either 1 or power of two.

[Flags]
enum F {
	A; // 1
	B; // 2
	C; // 4
}

var f: F = [ 'a' ];
var q: F = { b: true };

f += 'c'; // f.include('c')
f -= 'c'; // f.exclude('c')
f = f.toggle('c');
f = f.filter('c');
'c' in f;

// returns F value with all flags present
F.all;

// checks if flags are empty
f == []
f == undefined

Custom Numeric Type

Any numeric type can be used, including floating points. By default enum uses Int.

Use the context word wraps to use a custom numeric type for an enumeration.

enum E wraps BigInt {
}

Classes

class C {
	// constructor
	function C() {
		super();
	}

	// override instance method
	override function toString(): String (
		'violetscript'
	);

	// final instance method: cannot be overriden
	// by subtypes.
	final function finalMethod(): void {
	}
}

class F extends C implements I {
}

// generic class
class G.<T> {
}

// final class: cannot extend
final class Final {
}

Prohibit Object Initialiser

Use the DontInit decorator to prohibit object initializer on a specific class.

[DontInit]
class C {
}

var o: C = {}; // VerifyError!

Method Overriding

  • An override can specify either additional optional parameters or one additional rest parameter.
  • An override can specify a more specific return type: either a subtype or, if the original method's return is *, any different type.

Interfaces

interface I {
	// required method
	function rf(): void;

	// optional method
	function of(): void {
	}
}

// generic interface
interface G.<T> {
}

User Value Types

Value classes are passed by value. They can be defined by using the special Value decorator, as follows:

[Value]
class V {
	const x: Number;
	const y: Number;
}

Fields are all read-only. You can re-assign a field from an existing instance like this:

o = { x: newValue, ...o };

Array Types

The Array type is dynamically-sized.

The following line:

type ListOfNumbers = [Number];

is equivalent to:

type ListOfNumbers = Array.<Number>;

Tuple Types

Tuple types are passed by value. They are written as two or more type expressions enclosed by square brackets. For example:

type T = [Number, String];

Record Types

Record types are immutable structures passed by value. They are written in curly brackets. For example:

type R = {x: Number, y: undefined | RegExp};

A field is optional when it possibly contains undefined. The following types are equivalent:

type R1 = {x?: Number};
type R2 = {x: undefined | Number};

Field Order

The sequence of the record type fields is sensitive. A type {x: X, y: Y} differs from {y: Y, x: X}. This allows for organizing memory layout.

Function Types

Function types inherit the Function class.

// parameterless
type F1 = () => void;

// with required parameter
type F2 = (a: Number) => void;

// with optional parameter
type F3 = (a?: Number) => void;

// with rest parameter
type F4 = (...a: [Number]) => void;

Parameter Names

A type (a: T) => void differs from a type (_: T) => void because the parameter names differ.

Union Types

type U = undefined | Number | Boolean;

// "|" prefix

type XoY =
    | X
    | Y

Member Order

The sequence of the union members is sensitive. A type X | Y differs from Y | Z. This allows for organizing memory layout.

Type Parameters

Type parameters can specify constraints.

class G.<T: I> // T implements or extends I
	where T is K  // T also implements or extends K
{
}

Type Aliasing

Use the context keyword type for defining type aliases.

type AliasName = q.f.C;

type G.<T> = g.C.<T>;

Static Properties

Use the static modifier to define a property that is attached to the type rather than attached to its instance.

class C {
	static const x: Number = 10;
}

Expressions

Embed Expression

The context keyword embed can be used to include text or octet-stream of relative path to the current script.

var ba:ByteArray = embed './octetStream.bin';
var str:String = embed './someText.txt';

Await Expression

The await expression causes the surrounding function to be asynchronous, returning a Promise. It's currently not allowed in the top-level of a program.

Yield Expression

The yield expression causes the surrounding function to be a generator, returning Generator.

Default Expression

The default expression returns the default value of the given type.

default(Number); // 0

Function Expression

a => 10;
() => 10;

(function(): void {})();
(function(): Number (10 ** 2))();

Markup Expression

VioletScript supports markup similiar to XML, with the only exception of text nodes.

Remarks:

  • The markup syntax can be used to initialize any user class with the following restrictions:
    • The class must not have a DontInit decorator.
    • The class constructor must be either empty or receive only optional parameters and/or rest parameter.
  • A container class must implement IMarkupContainer.<T>.
  • A curly brackets into the markup must contain an expression of the array type.
const c = (
	<Container>
        <Item n=Infinity v={10**2} checked/>
        {childrenArrayRest}
    </Container>
);

// qualified tag name
<com.q.a.X/>;

// qualified tag name using colon
<s:Button/>;

const a: [Container] = <>
    <Container/>
    <!-- spread -->
    { [<Container/>,<Container/>,<Container/>] }
</>;

Object Initializer

Object initializer, written in curly brackets, supports shorthand property notation, trailling comma, spread components (...spreadObject) and a suffix type annotation.

Object initializer can only initialize:

  • * (instantiates an empty Object, not allowing spread)
  • Map
  • Flags enumeration
  • Record type
  • Class without a DontInit decorator

Additional semantics:

  • Using object initializer to initialize a plain object results in an object whose constructor property returns Object.
  • Spread elements are evaluated before fields when initializing flags, record or class, that is, anything other than Map.

Array Initializer

Array initializer, written in square brackets, supports ellisions, spread components (...otherArray) and suffix type annotation.

Array initializer can only initialize:

  • * (instantiates a [*] array)
  • Array
  • Set
  • Flags enumerations
  • Tuples

Spread

A spread component can either be a compatible Array, iterator or flags.

  • Flags: Flags type itself.
  • Any: Any at compile-time; at runtime it must be a compatible iterator or array, otherwise an error occurs.
  • Set: Iterator.
  • Array: Same array type or iterator.
  • Map: Unallowed.

Member Expression

o.x;

// optional member access in case o is undefined or null
o?.x;

If o is a package, then o.x can resolve to a subpackage in last attempt.

Index Expression

Unlike EcmaScript, the index expression, in the form o[k], uses an indexing mechanism that is separate from the object properties mechanism. This resolves conflict of properties and indexes. Methods such as Reflect.get(...) can be used in case a property must be dynamically resolved.

The index expression is indentation-aware.

o[k];

// optional index in case o is undefined or null
o?.[k];

Call Expression

Call expression is indentation-aware.

f();

// optional call in case f is undefined or null
f?.();

Special Behavior

  • If f is an enumeration, the call expression corresponds to a forced conversion from either String or numeric value to the enumeration.
  • If f is a type, but not an enumeration, it corresponds to the use of the new operator.
    • If f is the String type, it corresponds to a string conversion.
E(v)
T(ctorArguments)
String(v) // string conversion

Literal

Regular Expression Literal

/zxc/i

This Literal

this

String Literal

'violetscript'

Null Literal

null

Boolean Literal

false
true

Numeric Literal

0

Conditional Expression

c ? x : y;

Unary Expression

await f()
yield v
delete r[k]
typeof v
void v
!v
+v
-v
~v
v!
++v
--v
v++
v--
new C

Increment/Decrement Semantics

The increment and decrement operators can also be applied to expressions in the form x!, such as o[k]!++;.

Binary Expression

k in o;
k not in o;
x + y
x - y
x * y
x / y
x % y // remainder
x ** y
x && y
x ^^ y
x || y
x ?? y
x & y
x ^ y
x | y
x << y
x >> y
x >>> y
x == y
x != y
x === y
x !== y
x < y
x > y
x <= y
x >= y
nonDestructuringPattern = y
destructuringPattern = y
nonDestructuringPattern compound= y

v is T;
v is q : Q;
v instanceof T;

// optional conversion; both equivalent
v as T;
v as? T;

// forced conversion
v as! T;

Assignment

  • Assignment is only allowed as a statement, as a for update and as the right-hand side of another assignment.
  • It is allowed to use compound assigning applied to non-null index:
o[k]! += 1;

Parentheses Expression

(v)

List Expression

x, y

Generic Instantiation Expression

tOrFunction.<...>;

Super Expression

super.xOrF

Statements

Expression Statement

expression;

Empty Statement

;

Super Statement

super();

Import Directive

import q.u.*;
import q.**; // recursive; imports `q` and all subpackages
import q.u.B;
import A = q.u.B;
import Qu = q.u.*;

Import from global

The global identifier may be used to alias a property of the global package.

import GRegExp = global.RegExp;

Subpackage aliasing

A subpackage from a package can be accessed even when the base package is aliased.

// com.scientific.fns.f
import sci = com.scientific.*;
sci.fns.f();

If Statement

if (c)
{
	//
}
else
{
	//
}

Do Statement

do
{
	//
}
while (c);

While Statement

while (c)
{
	//
}

For Statement

for (i; c; u)
{
	// procedure
}
for (var i; c; u)
{
	// procedure
}
for (var i in o)
{
	// procedure
}
for (i in o)
{
	// procedure
}
for each (var i in o)
{
	// procedure
}
for each (i in o)
{
	// procedure
}

for..in should work with:

  • Object with proxy::iterateKeys

for each should work with:

  • Object with proxy::iterateValues
  • Iterable
  • Iterator
  • Any type (*)

Break Statement

break;
break someLabel;

Continue Statement

continue;
continue someLabel;

Return Statement

return;
return v;

Throw Statement

throw someError;

Labeled Statement

someLabel: for (;;)
{
}

Switch Statement

Unlike EcmaScript, the switch statement does not fallthrough in cases, which prevents general programmer bugs.

switch (d)
{
	case 10:
	{
		// procedure
	}
	default:
	{
		// procedure
	}
}

Switch Type Statement

switch type (d)
{
	case (n:Number)
	{
	}
	default
	{
	}
}

Include Directive

Combines statement sequence from a script with the current statement sequence.

include './anotherScript';

This also allows for partially implementing classes, similiar to C#'s partial classes. For example:

class C {
    var x: Number = 10;

    include './foo';
}

foo.vs:

function f(): void {
    trace(this.x);
}

Path resolution

  • You do not need to specify the .vs extension.
  • You do not need to specify index.vs when resolving to a directory.
// if './core' is a directory, the following lines
// are equivalent:
include './core';
include './core/index.vs';

Packages

It is allowed to define packages in an included script as long as it is included from a top-level context:

// foo.vs
include './bar';

// bar.vs
package q.b;

Visibility

include inherits the scope from where it is included.

// foo.vs
include './bar';

class C {
}

// bar.vs
var x: C?;

With Statement

with (o)
{
	// procedure
}

Use Namespace Directive

Opens namespace lexically.

use namespace someNS;

Use Resource Statement

Binds variables to block and automatically dispose resources through the use of the IDisposable interface.

use resource (disposable = obj)
{
	// procedure
}

Documentation Comments

VioletScript supports Markdown comments with special @ tags. They're called VioletDoc comments.

Here is an example:

/**
 * The function `f` does nothing.
 *
 * @example
 *
 * ```
 * f();
 * ```
 *
 * @throws {ArgumentError} If argument is invalid.
 */
function f(): void {
}

Supported Tags

  • @deprecated
  • @hidden
  • @example Example section.
  • @param paramName Description.
  • @return A return value.
  • @throws {C} Optional description.
  • @internal Internal comment.
  • @field {x} Field commment
    • Used internally when you add comment to a record field in a type alias to a record type.
    • It allows dot too for documenting subfields.
/**
 * [lexicalItemName]
 * [`lexicalItemName`]
 * [customLexicalItemName][lexicalItemName]
 */

Lexical Structure

Escape Reserved Words

var #catch: Number = 10;

Indentation-Aware String Literal

var s = '''
		Line 1
		Line 2
		''';

Hexadecimal and Binary Integer Literal

0x0A;
0b00010;

Packages

A package is a globally reusable namespace. The following program demonstrates basic use of package definitions:

package {
	// global package
	public const globalX: Number = 10;
}
package com.qux.bar {
	public function f(): void {
	}
	public function q(): void {
	}
}

globalX;
global.globalX; // equivalent

com.qux.bar.f();
com.qux.bar.q();
global.com.qux.bar.f();

// open com.qux.bar in lexical scope
{
	import com.qux.bar.*;
	f();
}

When curly brackets are omitted, all the following statements are part of the package:

package com.fun;

public function haveFun(): String (
    'haveFun'
);

Namespaces

namespace Q {
	function f(): void {
	}
}

Namespace Alias

namespace A = q.Q;

Variable Properties

// writable
var x: Number = 10

// read-only
const x: Number = 10

// type inference
const x = 10

Virtual Properties

function get x(): Number (
	10 ** 2
);

function set x(v) {
}

Functions

function f(): void {
}

function fWithRequiredParameter(a: Number): void {
}

function fWithOptionalParameter(a: Number = 10): void {
}

function fWithRestParameter(...a: [Number]): void {
}

function fWithExpressionBody(): String (
	'violetscript'
);

// native function
native function fNative(): void;

Generators

A function is a generator if it uses the yield operator.

Remarks:

  • Generator functions return a Generator object, which is an iterator.
  • A generator function finishes returning undefined.
function f(): Iterator.<Number> {
	yield 10;
}

const iterator = f(); // Iterator.<Number>
iterator .next(); // {done: false, value: 10}
iterator .next(); // {done: true, value: undefined}

Asynchronous Functions

A function is asynchronous if it uses the await operator.

Remarks:

  • Asynchronous functions return Promise.
  • Any thrown errors will reject the Promise.
function f(): Number {
	await a();
	return 10;
}

Generic Functions

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

Destructuring

Array Destructuring

Array destructuring syntax can in addition be used for tuples and user proxies.

[x, y] = someArray;

Record Destructuring

({x} = someObject);

Non-null assertion

You may get a verify error when destructuring from something that contains undefined or null. You can use exclamation (!) to assert the base is non-null and non-undefined:

const [{x!: {y}}!]! = o;

Decorators

A decorator is a function that either returns void or returns another function that returns void, applied to either a type, property or method as expressions enclosed by square brackets. Decorators cannot be applied everywhere.

[D(x = 10)] is equivalent to [D({x: 10})].

Note that 4 lexical names may be reserved in some contexts. Jump to the Reserved Lexical Decorators section below for quick information.

Decorators Applied to Types

function MyTypeDecorator(type: Class): void {
}

[MyTypeDecorator]
class C {
}

Decorators Applied to Properties

function MyFieldDecorator(o: C, {name}: Binding): void {
    trace(name);
}

class C {
    [MyFieldDecorator]
    public var x: Number;
}

Decorators Applied to Methods

function MyMethodDecorator(o: C, name: String): void {
    trace(name);
}

class C {
    [MyMethodDecorator]
    public function f(): void {
    }
}

Reserved Lexical Decorators

The following decorators are reserved, but can still be used if they do not directly appear as lexical references:

  • [Metadata()]
  • [Allow()]
  • [Warn()]
  • [FFI()]
  • When applied to a class definition, [Value] is reserved
  • When applied to a class definition, [DontInit] is reserved
  • When applied to an enum definition, [Flags] is reserved

You can still use them if they are under a namespace or package, such as q.b.Metadata.

Meta-data

Compile-time meta-data can be attached to any definition through the [Metadata()] decorator. It can contain any kind of data.

Any added meta-data is erased at runtime.

package q {
    [Metadata(x = '')]
    public function f(): void {
    }
}

Strictness

Shadowing variables

The "use shadowing" pragma allows shadowing all variables in a scope:

{
    'use shadowing'
    var x = ''
    var x = 0
}

Item Visibility

Items may specify a visibility modifier:

  • public
  • private
  • protected
  • internal

The default visibility is public.

Standard Built-in Objects

The standard built-in objects are not yet documented, but they will be similiar to JavaScript ones.

Initial VioletDoc documentation for them will be generated soon. Here are the draft sources.

Runtime

This section lists some runtime details and general optimizations.

Single Item Insertion

The following call takes only one argument, so do not create an throwaway Array for rest arguments:

array.push(v);

Constant Number Iteration

The following can be optimized by skipping the creation of the Generator instance and skipping handling the step parameter.

for each (var i in Number.range(0, 10)) {
    // procedure
}

String Representation

String is either original or a slice of another one. When the type is auto boxed, this is not the case.

Auto Boxing of Value Types

Value types are interned on auto boxing. Primitive types like Number are interned in more depth, for example, values under < 512 are looked up into a particular collection, Strings are divided into collections by length and Boolean is simply binary. These internation collections should preferably consist of weak references whenever possible in the host environment.

Any Type Representation

  • In native host environments, for the * type, a undefined value is identified by variant 0, null by variant 1 and anything else is an actual Object reference address.

Union Type Representation

Union types are structural, however they are differentiated by member order. The first variant starts by the number 0.

// undefined = 0
// RegExp = 1
type UoR = undefined | RegExp;

// RegExp = 0
// undefined = 1
type RoU = RegExp | undefined;

Math methods

Things like min(), max() and clamp() can be optimized according to given arguments ahead of time.

  • min() with 2 arguments
    • Specialized types as it takes *
  • max() with 2 arguments
    • Specialized types as it takes *
  • max() or min with a list of numeric arguments of the same type
  • clamp() with specialized types

Compiler Guide

Including and Excluding Sources

The compiler does not accept globbing patterns or specifying a directory for including sources recursively. This is so that you specify sources in the correct order for static property initialization.

Generally you must have an entry script including other scripts:

include './script1';
include './script2';