1. Introduction

This specification defines the N4JS language.

In general, the N4JS JavaScript dialect used is identical to the standard ECMAScript as defined in the 6th edition of ECMA-262, also known as ECMAScript 2015, referred to as [ECMA15a].

1.1. Notation

1.1.1. Grammar Notation

For the specification of the syntax and structure of elements, we use a slightly augmented similar to the grammar language of Xtext Grammar Language.

Similar to [ECMA11a], we define types with properties only for the purpose of explanation and usage within this specification. We use the Xtext notation style to assign values to meta-properties. Particularly, we use the Xtext notation for collection (+=) and boolean (?=) values. These properties are written in italics. Enumerations are defined similar to Xtext. In order to allow the specification of default values, which are often defined by omitting the value, we always define the literal explicitly if it can be defined by the user.

The following lists informally defines the grammar:

Terminal

Terminals (or terminal strings) are enclosed in single quotes, e.g., terminal.

Enumerations

Rules which contain only terminals used as values for properties are marked with enum for enumeration.

Properties

Values of non-terminals, e.g., other rules, can be assigned to properties. The property name and the assignment are not part of the original syntax and only used for the meta description. E.g., name=Identifier.

Collection Properties

If a property is a collection, values are added to that list via +=. E.g.,property+=Value .

Boolean Properties

Boolean properties are set to false by default, if the value (usually a terminal) is found, the boolean value is set to true. Often, the name of the property is similar to the terminal. E.g., final?='final'?.

Properties of a non-terminal are sometimes listed again below the grammar. In that case, often pseudo properties are introduced which are derived from other properties and which are only used for simplification.

1.1.2. Type Judgments and Rules and Constraints Notation

1.1.2.1. Typing Rules and Judgments

Definition: Rule

We use the common notation for rules such as type inference rules [1], that is

premisesconclusionrule name

premises is the rule’s premises (e.g., the expression to be inferred), conclusion the result of the rule. rulename is an optional condition which may be omitted.

Both parts of the rule may contain multiple expressions, which are concatenated via 'and'.

For example, the following

P1P2P3C

can be read as

if P1, P2, and P3 are all true, then C is true as well.

The following judgments (with relation symbols) are used:

subtype <

-

type :

in which the left hand side is a declaration or expression, and the right hand side a type. We also use [[...]] as a function returning the (inferred) type of an expression.

expectedTypeIn :

a relation with three arguments: containerexpression:type means, that expression is expected to be a subtype of type inside container

The following statement, for example, defines transitivity of subtypes (in a simplified manner):

ΓB<:AΓC<:BΓC<:A

is the context containing (bound) type variables etc., can be read as entails. Thus, the rule can be read as follows:

if the type B is a subtype of type A in context Γ (i.e. with constraints on type variables specified in Γ), and if type C is a subtype of B, then C is also a subtype of A in context Γ.

In rules, we sometimes omit the environment if it is not needed. New information is sometimes added to the environment, in particular, substitutions (that is binding type variables to a type). The set of substitutions is written with θ (theta). If new substitutions are explicitly added to that set, we write θVT (V is substituted with type T). Often, these bindings are computed from a parameterized type reference which declares type arguments which are bound to the type variables of the generic declaration. In this case we simply write θp, in which p is the parameterized type declaration. As these new substitutions must become part of a (newly) created environment, we then usually write Γθp. These substitutions are usually omitted.

1.1.2.2. Types of an Element

A variable or other typed element may be associated with three types:

  1. Declared type: the type explicitly specified in the code, e.g., var s: string.

  2. Inferred type: the type inferred by the type inferencer, e.g., var s = "Hello" infers the type of s to string. I.e. Γs:string will be true, or [[s]]<:string. If an element is annotated with a type ,i.e. it has a declared type, the inferred type will always be the declared type.

  3. Actual type: the actual type of a variable during runtime. This type information is not available at compile time and ignored in this specification.

These types are not type declarations but type references, in fact, as they may be parameterized. For the sake of simplicity, we often omit the Ref suffix to shorten formulas. Consequently, we define the following properties and pseudo properties for typed elements such as variables:

declaredTypeRef

The explicitly declared type, this is usually a real property of the construct. Not all elements allow the specification of a declared type, such as expressions.

inferredTypeRef or [[...]]

This pseudo property is the inferred type computed by the type inferencer.

typeRef

A pseudo property for elements with a declaredType property. It is similar to the inferred type, i.e. e.type=[[e]]

1.2. Auxiliary Functions

This section describes some auxiliary functions required for definition of type inference rules later on.

1.2.1. Binding

Binding an identifier (variable reference) to a variable declaration (or variable definition) is not part of this specification as this is standard ECMAScript functionality. However, some valid ECMAScript bindings are permitted due to visibility constraints.

Definition: Binding Relation

We define a pseudo relation

bind:VariableReference×VariableDeclaration

which binds a reference, i.e. an identifier, to a declaration (e.g.,variable declaration).

Binding of variable references to declaration is defined by ECMAScript already. Type references only occur in type expressions, how these are handled is explained in Type Expressions.

We usually omit this binding mechanism in most rules and use the reference similarly to the declaration or definition it is bound to. If a variable reference r, for example, is bound to a variable declaration D, i.e. bindrD, we simply write r.type instead of bindrD,D.type to refer to the type expression (of the variable).[2]

A DeclaredType references the type declaration by its simple name that has been imported from a module specifier. We define the method bind for declared types as well:

We define a pseudo relation

bind:DeclaredType×Class|Interface|Enum

which binds a type reference, i.e. a simple name, to the type declaration.

1.2.2. Merging Types

In some cases we have to merge types, e.g., types of a union type or item types of an array. For that purpose, we define a method merge as follows.

Definition: Merge Function

We define a pseudo function

merge:Type×...×TypePType

The idea of this function is to remove duplicates. For example; if a union type contains two type expressions te1 and tek, and if τte1=τte2, then mergeτte1τte2 contains only one element. The order of the elements is lost, however.

1.2.2.1. Logic Formulas

In general, we use a pragmatic mixture of pseudo code, predicate logic, and OCL. Within constraints (also within the inference rules), the properties defined in the grammar are used.

In some rules, it is necessary to type the rule variables. Instead of explicitly checking the metatype (via μX=:MetaType), we precede the variable with the type, that is: MetaTypeX.

Instead of "type casting" elements, often properties are simply accessed. If an element does not define that element, it is either assumed to be false or null by default.

If a property p is optional and not set, we write p=null to test its absence. Note that p=null is different from p=Null, as the latter refers to the null type. Non-terminals may implicitly be subclasses. In that case, the concrete non-terminal, or type, of a property may be subject for a test in a constraint.

1.2.3. Symbols and Font Convention

Variables and their properties are printed in italic when used in formulas (such as rules). A dot-notation is used for member access, e.g. v.name. Also defined functions are printed in italic, e.g., accrD. Properties which define sets are usually ordered and we assume 0-indexed access to elements, the index subscripted, e.g., v.methodsi.

We use the following symbols and font conventions:

, , , ¬

Logical and, or, exclusive or (xor), and not.

, , if..., then..., else...

Logical implication, if and only if, and if-then-else.

true, false, null,

Boolean true, boolean false, null (i.e., not specified, e.g., v.sup= means that there are is no sup (super class) specified), empty set.

, , , , |x|

Element of, not an element of, union set, intersection set, cardinality of set x.

PX

Power set of X, i.e. PX=U:UX.

, ,

Exists, not exists, for all; we write x,...,z:Px...z and say

"there exists x,...,z such that predicate P is true".

Note that x:Pxx:¬Px.

μ...

(mu) read "metatype of"; metatype of a variable or property, e.g.,

if...μx=:Classthen...else...
x¯

Sequence of elements x1,...,xn. E.g., if we want to define a constraint that the owner of a members of a class C is the class, we simply write

C.members¯.owner=C

instead of

mC.members:m.owner=C

or even more complicated with index variables.

Sequences are 1-based, e.g., a sequence s with length |s|=n, has elements s1,...,sn.

Quick Links