April 2002 Draft
JavaScript 2.0
Core Language
Expressions
|
Tuesday, December 18, 2001
Most of the behavior of expressions is the same as in JavaScript 1.5. Differences are highlighted below. One general difference is that most expression operators can be overridden.
The above keywords are not reserved and may be used in identifiers.
Just like in ECMAScript Edition 3, an identifier evaluates to an internal data structure called a reference. However, JavaScript
2.0 references can be qualified by a qualifier, which, in the general syntax, is a ParenExpression
that must be a compile-time constant expression that evaluates to a namespace. For convenience,
if the ParenExpression consists of a single identifier, the parentheses
may be omitted: (a)::m
may be written as a::m
.
The reserved words public
and private
may also be used as qualifiers. public
evaluates
to the public
namespace. internal
(which is not a reserved
word) evaluates to the containing package’s anonymous namespace. private
can only be used inside a class and evaluates to the containing class’s anonymous namespace.
See the name lookup section for more information on the ::
operator.
A Number literal, ParenListExpression, or UnitExpression followed by a String literal is a unit expression, which is evaluated as follows:
Evaluate the String literal to obtain a string S. Parse that string according to
the unit grammar and semantics to obtain
a list of identifiers and exponents [id1e1,
id2e2, ..., idnen].
If the parse fails, signal a syntax error. If the list is empty, let U be the function nullUnit
, which
accepts one required and one optional argument and returns its first argument, ignoring the optional argument.
If the list is not empty, for each i between 1 and n, let Vi be the value of
looking up the class property idi of the Unit
class. If ei is 1,
let Fi = Vi; otherwise, let Fi = Vi.pow(
ei)
.
Then let U = F1*
F2*
...*
Fn.
For example, if S is "Kg*m^2/s^2*q"
, then U is the value of Unit.Kg*Unit.m.pow(2)*Unit.s.pow(–2)*Unit.q.pow(–1)
.
The result U should be callable as a function that accepts one required and one optional argument. The unit
expression calls U, providing the numeric value of the Number literal, ParenListExpression,
or UnitExpression as the first argument. The second argument is present
only for the UnitExpression Number [no line break] String
production, in which case it is the original Number literal expressed as a string. Continuing
the example above, the unit expression 32.50 "Kg*m^2/s^2*q"
, evaluates to the result of (Unit.Kg*Unit.m.pow(2)*Unit.s.pow(–2)*Unit.q.pow(–1))(32.5, "32.50")
.
U’s second argument allows user-defined unit classes to define extended syntaxes for numbers. For instance,
a long-integer package might define a unit called "L"
that treats the Number literal
as a full 64-bit number without rounding it to a float64 first. Such a unit can be combined with other units by listing the
units one after another; note that the lexer allows the first unit to be unquoted if it directly
follows the number: 3L "cm"
is the same as 3 "L" "cm"
and evaluates to the result
of Unit.cm(Unit.L(3, "3"))
.
The Unit
class is predefined by the system. A program can define additional units by using a class
extension. The unit
attribute is conveniently provided to make
a definition into a class extension. For example:
unit const µm = Unit.m/1e6; unit const Å = Unit.m/1e10; unit const dm = Unit.m/10; unit const \_in = Unit.m*0.0254; unit const liter = Unit.dm.pow(3);
\_
must be used to define the unit in
because in
is a reserved word. However, the
unit in
may be used without quoting it, as in the expression 3in + 5cm
.
null
true
false
public
this
public
evaluates to the public
namespace.
this
may only be used in methods, constructors,
or functions with the prototype
attribute set.
A FunctionExpression creates and returns an anonymous function.
A ParenExpression must evaluate to a string.
super
, which may only be used inside a class C, can be applied to a subexpression that evaluates
to an instance v of C. That subexpression can be either a ParenExpression
or omitted, in which case it defaults to this
.
As specified in the grammar below, the SuperExpression must be embedded as an operand to one of the following operators:
.
(property lookup), []
(indexing), ()
(call), new
,
new()
, or in
operator (for the ()
and new()
operators the SuperExpression
must be a FullSuperExpression)+
, -
, ~
, ++
, or --
operator+
, -
, *
, /
, %
, <<
,
>>
, >>>
, |
, ^
, &
, <
,
>
, <=
, >=
, ==
, !=
, ===
, or !==
operator+=
, -=
, *=
, /=
, %=
, <<=
,
>>=
, >>>=
, |=
, ^=
, or &=
operatorsuper
changes the behavior of the operator in which it is embedded by limiting its property search to definitions
inherited from class C’s superclass. See property lookup and
operator dispatch.
++
--
A SimpleQualifiedIdentifier or ExpressionQualifiedIdentifier expression id resolves to the binding of id in the innermost enclosing scope that has a visible binding of id. If a qualifier q is present before the id, then the QualifiedIdentifier expression resolves to the binding of id in the innermost enclosing scope that has a visible binding of id in the namespace q.
The .
operator accepts a QualifiedIdentifier as the
second operand and performs a property lookup.
The []
operator can take multiple (or even named) arguments. This allows programmers
to define data structures such as multidimensional arrays via operator overriding.
Unless the []
operator is overridden for an object o, the expression o[
m]
explicitly coerces m to a String
s
and returns the result of o.
s, limiting the name lookup to the public
indexable
properties of o. See property lookup.
A list of arguments can contain both positional and named arguments. The positional arguments are the subexpressions separated by commas within the ListExpressionallowIn. Named arguments use the same syntax as object literals. All strings except those consisting entirely of digits are legal for argument names. No two arguments may have the same name. The order of named arguments is immaterial.
The typeof
operator is deprecated. It returns a string as in JavaScript 1.5. To get the
type of an object x, use x.class
instead.
The expression a is
b takes an expression that must evaluate
to a type as its second operand b. When a is not null
, the expression a is
b
returns true
if a is a member of type b and false
otherwise; this is equivalent to testing whether a can be stored in a variable of type b without coercion.
When a is null
, a is
b behaves analogously to method
dispatch and returns true
if b is either Object
or Null
and false
otherwise. As a special case, when a is –0.0 and b is sbyte
, byte
, short
,
ushort
, int
, or uint
, a is
b returns true
.
The expression a as
b returns a if a
is a member of type b. Otherwise, if a can be implicitly
coerced to type b, then the result is the result of that implicit coercion. Otherwise, a as
b
returns null
if null
is a member of type b or throws an exception otherwise. In any case
b must evaluate to a type.
The instanceof
operator is deprecated and behaves in the same way as in JavaScript
1.5 — a instanceof
b follows a’s prototype chain.
The ^^
operator is a logical exclusive-or operator. It evaluates both operands. If they both convert to true
or both convert to false, then ^^
returns false; otherwise ^^
returns the unconverted value of whichever
argument converted to true.
A compile-time constant expression is an expression that either produces an error or evaluates to a value that can be determined at compile time.
The reason that a compile-time constant expression is not guaranteed to always evaluate successfully at run
time is that global name lookup cannot be guaranteed to succeed. It is possible for a program to import a package P
that defines a global constant P::
A that can be accessed as A and then dynamically
define another top-level variable Q::
A that collides with A. It does not appear
to be practical to restrict compile-time constant expressions to only qualified names to eliminate the possibility of such
collisions.
A compile-time expression can consist of the following:
null
, numeric, boolean, and string constants.+
(unary and binary), -
(unary and binary), ~
, !
,
*
, /
, %
, <<
, >>
, >>>
,
<
, >
, <=
, >=
, is
, as
, in
,
instanceof
, ==
, !=
, ===
, !==
, &
, ^
,
|
, &&
, ^^
, ||
, ?:
, and ,
as long
as they are used only on numbers, booleans, strings, null
, or undefined
.A pure function cannot have any read or write side effects or create any objects. A pure function’s result depends only on its arguments. A JavaScript host embedding may define some pure functions. Currently there is no way for a script to define any such functions, but a future language extension may permit that.
A reference R to a definition D of a compile-time constant is allowed inside a compile-time constant
expression as long as the conditions below are met. If D was imported from another package, then the location of
D is considered to be the location of the import
directive. If D is visible to R
by virtue of an intervening use
directive U, then the conditions below have to be satisfied both with
respect to D and R and with respect to U and R.
use
directive between
D and R.Some compile-time constant expressions only allow references to definitions that are textually prior to the point of the reference. Other compile-time constant expressions allow forward references to later compile-time constant definitions.
JavaScript 2.0 imposes the following restrictions:
const
definitions with the compile
attribute must have initializers that are compile-time
constant expressions. These expressions cannot contain forward references.import
, class
, interface
, include
,
and namespace
directive must dominate the end of the program or package. This restriction
limits these statements to the top level of the program, a top-level block, or a top-level conditional whose condition
is known at compile time.class
or interface
definition.A statement A dominates statement B if any of the following conditions are met:
case
or default
labels between them.false
, and C dominates B.Note that the above definition is conservative. If statement A dominates statement B, then it is guaranteed that, if B is executed then A must have been executed earlier; however, there may be some other statements A' that also are guaranteed to have been executed before B but which do not dominate B by the above definition.
A statement A is dead if any of the following conditions are met:
false
.break
, continue
, return
, or throw
statement B
such that statements A and B are in the same block with B before A and no
case
or default
labels between them.Note that the above definition is conservative. If a statement is dead, then it is guaranteed that it cannot be executed; however, there may be statements that cannot be executed that are not dead by the above definition.
A statement is live if it is not dead.
Waldemar Horwat Last modified Tuesday, December 18, 2001 |