You are currently viewing a snapshot of www.mozilla.org taken on April 21, 2008. Most of this content is highly out of date (some pages haven't been updated since the project began in 1998) and exists for historical purposes only. If there are any pages on this archive site that you think should be added back to www.mozilla.org, please file a bug.




ECMAScript 4 Netscape Proposal
upnext

Monday, June 30, 2003

A multi-page version of this document is also available.


This document is Netscape’s proposal to the ECMA TC39TG1 working group for the ECMAScript Edition 4 language. This proposal is being updated continuously to match issues resolved in the committee discussions. The TC39TG1 working group’s current schedule calls for a release of a ECMAScript Edition 4 standard sometime in 2002.

See also a draft of the specification under construction in PDF format.

JavaScript 2.0 is a slight superset of ECMAScript Edition 4. JavaScript 2.0 contains features that were considered for ECMAScript Edition 4 but for which more experience is desirable before standardization occurs.

Contents

Changes

The following are recent major changes in this document:

Date Revisions
Jun 30, 2003
Jun 4, 2003
  • Split the super semantic field into super for classes and archetype for prototype-based objects.
  • Implemented simple prototypes on most objects, including class objects themselves, for compatibility with JavaScript 1.5.
  • Changed semantics so that the enumerable attribute now inherits when overriding an instance property. This was the original intent, although it was ambiguous in the written description of the attribute.
  • Put back a very simple package definition/import mechanism. Moved the extended import directive with the include/exclude selection mechanism to the rationale.
  • Added semantics for the library classes Object, Void, Null, Boolean, GeneralNumber (except for number formatting), Number, float, sbyte, byte, short, ushort, int, uint, long, ulong, Character, String (except for regular expressions), and Namespace.
  • Added numerous semantic utility functions to support the above classes.
  • Added \U escapes to the lexical grammar (but not yet the semantics).
  • Removed include and exclude keywords.
  • Removed some obsolete text from the description of Types; however, that page still needs a little work as it’s not as up-to-date as the semantics.
May 22, 2003
  • Renamed uses of the word “member” to “property” in the semantics. Cleaned up many other names without changing the meaning of the semantics.
  • Renamed Character to Char16, as well as related semantic names. Updated the description of Char16.
  • Implemented conversions of strings to numbers in the semantics. Appended an extra start symbol to the lexical grammar and semantics to assist with these conversions.
  • Eliminated the class Prototype, merging it with the class Object. This required modifying the rules on which objects can be used as prototypes — only direct instances of Object or other prototype-based classes can now be used as prototypes.
  • Made Object be dynamic, which required a slight change to the rules of inheritance of the dynamic attribute — it is now inherited from any superclass except Object.
  • Fixed instanceof when the second operand is a class such as RegExp and Array.
  • Implemented simple prototypes on primitive classes for compatibility with ECMAScript Edition 3.
  • Removed SystemFrame, which wasn’t needed any more.
  • Fixed errors in grammar rules for private.
  • Implemented final and static as attributes instead of syntax.
  • Removed obsolete semantic utilities and added a few helpers for constructing the standard classes.
  • Defined the global object, namespaces, attributes, and classes.
  • Changed the syntax of semantic record constructors to use single angular braces instead of double angular braces.
May 2, 2003
  • Updated the semantics. Simplified some of the object data structures. Merged uninitialised with none. Defined class initialization. Defined getters and setters. Implemented instanceof.
  • Defined error classes and replaced error tags with these classes. Revised which error gets reported in many cases.
  • Renamed class semantic variables from names such as objectClass to Object.
  • Removed package, import, and export directives. The description of packages and imports remains in the packages page, but is non-normative and there are no detailed semantics for these constructs.
  • The return statement cannot return a value from a constructor or a setter.
  • A getter must return a value; it cannot fall off the end of the function.
Apr 2, 2003
  • Removed multiple constructors from the body of the proposal and moved them to the rationale for future consideration.
  • Removed the constructor attribute.
  • Replaced the invoke superconstructor case with the ... function call syntax for passing an array of arguments (similar to what apply does) to any function or constructor.
Mar 24, 2003
  • Updated the semantics. Implemented is, catch, for, and for-in. Implemented instance methods on classes.
  • Disallowed initializers on for-in target variables. These are allowed by the grammar for convenience but prohibited by the semantics’ validation.
  • Fixed a major const definition design flaw in the semantics that led to race conditions and the possibility of definitions not being set up properly or initialized several times. Replaced the design, which relied on error catching to detect non-constant expressions at compile time, with one that lazily evaluates forward-referenced const expressions.
  • The prototype attribute now applies only to functions to make them into prototype-based functions/constructors. Removed its usage to make extractable class members.
  • Removed the old usage of the word “generic”.
  • Changed the enumerability defaults to match ECMAScript Edition 3.
  • Removed the static path analysis from the calling a superconstructor section.
  • Added invoke case to calling a superconstructor to allow passing a variable number of arguments to a superconstructor. (invoke is the same as apply except that it doesn’t take a this parameter.)
  • Required initializers for instance members to be compile-time constant expressions to simplify the semantics and avoid issues about how many times they are evaluated.
  • Minor editorial fixes.
Feb 17, 2003
  • Updated the semantics. Reorganized the property read, write, and delete code and indirected it through six customizable internal methods.
  • Merged packages with global objects in the semantics.
  • Introduced ilong and iulong notation in the semantics.
  • Introduced ns::id notation in the semantics.
  • Merged the concepts of inaccessible and uninitialized variables in the semantics.
  • Fixed the handling of const variable initializers in the semantics.
  • Implemented with statements and array initializers and updated object initializers in the semantics. These resulted in slight grammar changes.
Jan 29, 2003
  • Removed named function parameters from the body of the proposal and moved them to the rationale for future consideration.
  • Significantly simplified the semantics by removing named function parameters.
  • Class constructors no longer take named parameters to initialize instance members.
  • Allowed dynamic properties with namespaces to better integrate with E4X.
  • Removed typed arrays from the body of the proposal and moved them to the rationale for future consideration.
Jan 24, 2003
  • Updated the semantics.
  • Simplified the override logic by introducing the restriction that an instance variable cannot override a getter or a setter. The reverse is still allowed — a getter may override either a getter or a virtual instance variable, and a setter may override either a setter or a virtual instance variable. Also, the overriding definition may no longer override two different base members or add namespaces not mentioned in the base member.
  • An overriding method definition may no longer change the signature.
  • Allowed run-time namespace expressions in qualified identifiers to harmonize this proposal with E4X.
  • Fixed typos and anachronisms.
Jan 13, 2003
Nov 19, 2002
  • Updated the semantics.
  • Reorganized the grammar productions for function definitions and try statements without materially changing the language. This helped better factor the semantics for these constructs.
Oct 29, 2002
  • Changed numeric arithmetic contagion rules to try to return a long or ulong value when at least one operand is a long or ulong; if that’s not possible, the result overflows to a Number. Division will return a Number if that result would be more precise than a long or ulong.
  • Removed floating-point float arithmetic operations other than unary negation; these operations will always return a Number (or possibly a long or ulong if the other operand is a long or ulong).
  • Made === ignore differences in numeric types, so 5.0 === 5L will now be true.
  • Reduced repetitive semantics: when all actions A on a nonterminal N merely call A on the nonterminals in N’s grammar expansions, the actions are now abbreviated.
  • Simplified the lexer and stages descriptions.
  • Fixed several typos.
Sep 25, 2002
  • Fixed Number and float functionality.
  • Added conversions from numbers to strings.
  • Fixed broken links.
Sep 20, 2002
  • Operator overriding has been deferred until a future version of the language. Removed it from the proposal and moved it to the rationale section.
  • Removed the .() operator again.
  • Unit support has been deferred until a future version of the language. Removed it from the proposal and moved it to the rationale section.
  • Added semantics for float support.
  • Added long, ulong, and float literals.
  • Removed the concept of indexable members. All public members are now accessible via [].
  • Revised the lexical and syntactic grammar and semantics.
Jun 18, 2002
  • Attributes are now idempotent.
  • Removed the abstract attribute.
  • The enumerable attribute implies public.
  • Namespaces and classes defined as members of another class must specify the static attribute.
  • Added requirement that each variable referenced in a const initializer be resolvable to a scope.
  • Restricted class and namespace definitions to only global, package, or class scopes or blocks nested inside these scopes.
  • Corrected wording discrepancy: the superclass of a class must be a compile-time constant expression without forward references.
May 17, 2002
  • Removed class extensions from the proposal and added them to the rationale.
  • Since class extensions are now gone, made unit lookup look in the unit namespace instead of the Unit class.
  • Simplified the discussion of definition scopes, since no scope attributes remain in the proposal.
  • Deleted wrap pragma for wraparound machine integer arithmetic.
  • Deleted the compile attribute. Constants defined using const automatically become compile-time constants when used in compile-time constant expressions.
  • Defined explicit coercions for integral machine integers.
  • Removed restriction that top-level definitions in a package can only be placed in the namespace public or in namespaces defined within that package.
  • Added interfaces to the rationale.
Apr 22, 2002
  • Corrected and clarified the rules for when a variable or a constant may be accessed. Except for type- and attribute-less definitions, constants and variables are accessible only after being defined. Constants may be written at most once. Type expressions must be compile-time constant expressions.
  • A constant and a setter with the same qualified name may not be defined in the same scope.
  • Hoisting does not occur into class scope (this possibility is now excluded by the definition of a regional scope).
  • explicit is now an attribute instead of a specialized namespace.
  • Corrected the interaction of overriding and namespace defaulting in a definition. Defined the namespace defaulting and overriding rules.
  • Replaced mayOverride by override(undefined). Added regular rules for the behavior of overriding and the override attribute.
  • Allowed uninitialized semantic record fields.
  • Continuing to write the formal semantics. Added semantics for constant and variable declarations.
Mar 4, 2002
  • Restricted pragma arguments to be literal booleans, numbers, or strings. Also, strict mode changes now affect the semicolon after the pragma.
  • Removed class declarations without a body.
  • Removed include/exclude clauses from use directives. These now apply only to import directives and now indicate which top-level properties are shared. Simplified the name lookup algorithms to not account for includes/excludes on individual use directives.
  • Made other minor grammar changes to share productions and their semantics without affecting the language.
  • Added for each to the list of computation steps.
  • Added abbreviated notation for copying most of the fields from an existing tuple or record into a newly constructed one.
  • Continuing to write the formal semantics. Made major changes and additions to the formal semantics for evaluation of expressions and statements. Reorganized object layout and unified frames with container objects. Defined package objects. Added a phase parameter to distinguish compile-time constant expressions from runtime expressions.
Jan 4, 2002
  • Removed attributes from pragmas. Although somewhat useful, this made parsing dependent on attribute evaluation, and I'd rather not have such a dependency in the language.
Dec 20, 2001
  • Regularized the grammar of statements and directives. Separated annotated blocks into groups of either directives or substatements, which eliminated the troublesome situation of a definition being located in a substatement that happens to be a group. The attributes on a group of substatements are now restricted to only true and false.
Dec 19, 2001
  • Removed the local attribute, which was no longer necessary for anything.
  • Added the compile attribute to explicitly mark which const definitions must be compile-time constants. This became necessary because the semantics of compile-time const definitions are subtly different from those of regular const definitions, and it became unwieldy to try to guess which one a const definition is, based on how it is used.
  • Revamped the description of compile-time constant expressions. Removed the references to dominators. There are now two kinds of compile-time constant expressions: ones that allow forward references and ones that don’t.
Dec 17, 2001
  • Disallowed nested labels with the same name in the same function in the semantics.
  • Renamed GoContinue to Continue, GoBreak to Break, GoThrow to ThrownValue, and GoReturn to ReturnedValue in the semantics.
  • Pragmas may now take attributes (true and false only).
  • Fixed outdated text in the description of function parameters.
Dec 6, 2001
  • Reverted to the old definition of the Integer type; it’s back to being the set of integral IEEE doubles.
  • Deleted the double type and made Number act as double once again.
  • Renamed Character to char.
  • Rewrote the machine types section once again. Renamed int8, uint8, int16, uint16, int32, uint32, int64, and uint64 to sbyte, byte, short, ushort, int, uint, long, and ulong respectively. The first six of these are now subtypes of Integer; long and ulong are now disjoint from Number.
  • Renamed “coercion” to “implicit coercion” and “cast” to “explicit coercion”.
  • Added special case to the is operator to treat –0.0 as though it were +0.0.
  • Made the as operator support implicit coercions.
  • Added implicit coercions to the concept of a type.
Nov 26, 2001
  • Added support for comments inside functions in the semantics. Removed invariant and steps and turned them into comments.
  • Temporary variables in the semantics can now be declared without assigning a value to them.
  • Added more syntactic semantics.
  • Removed the #, ->, .., and @ tokens.
  • Made the Character type distinct from the set of single-character Strings.
  • Made double be a subtype of Number, which is now the union of numeric types. Made Integer distinct from double and accept unlimited-precision integers. Unified the integral machine types with Integer.
  • Made integral conversions that can result in loss of range error-checked by default. Added the wrap pragma to convert the behavior to wraparound.
Oct 26, 2001
Oct 18, 2001
Oct 16, 2001
Oct 3, 2001
  • Updated semantic notation to combine action procedures for multiple expansions of a nonterminal into one action procedure. Also deleted the notation for simple procedures to reduce confusion.
  • Reformatted the formal semantics and incorporated the above notation changes.
  • Added indexes to the rtf files.
Sep 26, 2001
  • Added more semantics.
  • delete now takes a PostfixExpression instead of a PostfixExpressionOrSuper.
Sep 24, 2001
Aug 22, 2001
Aug 17, 2001
Aug 15, 2001
Aug 10, 2001
Jul 24, 2001
  • null is now a valid String value, distinct from the empty string.
  • Removed the const Array and const t types. Added the StaticArray[t], DynamicArray[t], ConstArray, and ConstArray[t] types.
  • Removed the t[] notation. This is now reserved for fixed-size machine array types.
  • Removed the const operator.
  • Instance member initializers are re-evaluated each time a new instance is created.
  • Toughened the rules for argument type matching in overrides.
  • Changed the variable lookup rules to make a class’s instance members visible but inaccessible in static methods.
  • Removed the empty argument list case from the PragmaExpr and UseDirective grammars.
  • Removed the * wildcard case from IncludesExcludes.
  • Regularized the ImportDirective grammar.
  • Enabled true and false attributes on use namespace directives. Restricted import directive attributes to only true and false.
  • Simplified various grammar productions and renamed grammar nonterminals without affecting the language. Folded the Definition production into the Directive productions.
  • Specified extent of pragmas.
Jun 29, 2001
  • Renamed instanceof to is to allow it to have the new, more sensible semantics without breaking ECMAScript Edition 3 programs. instanceof is not part of ECMAScript Edition 4 but can be supported for legacy programs. is is a new reserved word.
  • Removed the [no line break] constraints after use.
  • Updated the compatibility section.
Jun 15, 2001
  • Renamed the machine types byte, ubyte, short, ushort, int, uint, long, ulong to int8, uint8, int16, uint16, int32, uint32, int64, and uint64 respectively.
  • Renamed Tuple to const Array.
  • Removed the nonindexable and nonenumerable visibility modifier attributes. Updated the definitions of the indexable and enumerable attributes.
  • Removed the indexable attribute keyword, but the indexable concept still exists because it is needed for compatibility with ECMAScript Edition 3.
  • Changed the default for public definitions to nonindexable and non-enumerable.
  • Forbade conflicting or repeated attributes.
  • The default member modifier attribute for const definitions is now final instead of static.
  • Removed the requirement to use this. to initialize instance constants from a constructor.
  • Setters can no longer return a value; the result of an assignment expression is now always the value of its right side.
  • Only classes with at least one member with the prototype attribute will themselves have a predefined prototype global member.
  • Renamed language directives to pragmas and removed the list of JavaScript versions.
  • Simplified the syntax of pragmas and changed the syntax of pragmas, namespace uses, and imports to make them consistent. Eliminated noninsertable semicolons.
  • Removed the parenthesized expression format of FieldName.
  • Noted that an implementation does not have to support package circularities but may do so as an extension.
  • Interchanged the names of the Definition and AnnotatedDefinition nonterminals.
  • Renamed the parser grammar to the syntactic grammar and the lexer grammar to the lexical grammar for consistency with ECMAScript Edition 3.
Apr 11, 2001
  • Reincarnated the @ operator as the as operator, which is now a new reserved word and has a lower precedence to match instanceof. The as operator now returns null if the type doesn’t match but the destination type contains null. as does not do any coercions.
  • Removed the | syntax for indicating named parameters. All optional parameters are now also named.
  • The arguments local variable is now supported only for unchecked functions.
  • Modified the syntax of the semantics to better match the ECMAScript Edition 3 style. Partially updated the notation page.
  • Fixed a bug in the unit semantics.
  • Note: Not all of the decisions reached in the last two ECMA TC39TG1 meetings have been integrated into this document yet.
Mar 9, 2001
  • Added links to the preliminary draft of the specification in PDF format.
  • Minor editorial changes.
Mar 2, 2001
Feb 28, 2001
  • Created the syntactic semantics. It’s still a work in progress.
  • Updated the execution stages page.
  • Added mutable cells and the associated operators to the semantic notation.
  • Renamed the Double semantic type to Float64 (this affects the semantics only, not the ECMAScript language).
  • Added many Float64 manipulation functions.
  • Made minor stylistic changes throughout the document.
Feb 22, 2001
  • Renamed type None to Never.
  • Renamed the empty escape from \Q to \_.
Feb 8, 2001
  • Added generic class members.
  • Extended the prototype attribute to allow any value for the this parameter and cause an instance member to appear in the class’s prototype global property. This makes prototype methods appear to be "intentionally generic" as per ECMAScript Edition 3.
Feb 6, 2001
  • Updated the definition extent model as discussed at the January TC39TG1 meeting. Now definitions are local by default but hoist if necessary. Removed the regional and global attributes.
  • Updated the definition conflict rules: added the rule about non-interfering local definitions and removed the rule about permitting re-execution of a const definition, since that is now impossible.
  • Removed the paragraph about ECMAScript Edition 4 being firmly in the dynamic camp from the introduction, since the examples given — dynamically defining classes and functions by placing them inside an if statement — no longer apply. Some of this can still be done using boolean attributes, but these attributes must be compile-time expressions.
  • Changed the data structures returned by methods of the for-in iteration protocol to be objects with named properties value and state rather than arrays or tuples with numbered properties 0 and 1. This allows the two properties to be declared using different types.
  • Specified a default superconstructor call if a constructor contains no other superconstructor calls.
  • Specified that object construction follows the Java model rather than the C++ model — when a constructor calls a virtual method on an object o under construction, it sees the most derived method even if the class in which that method is located has not yet run its constructor on o.
  • Added vector comprehensions [g(a| a  u] and [g(a| a  u and c(a)] to the semantic notation.
Jan 31, 2001
  • Eliminated namespace inheritance to simplify the proposal. This is unnecessary given the ability to use const to combine several namespace attributes.
  • Removed property lookups using a class before the :: (x.C::n and super x.C::n where C is a class). This feature did not add much useful functionality.
Jan 25, 2001
  • Minor wording changes.
  • Brought back the .() operator.
  • Updated semantic notation — revised description and usage of and added unique id’s.
Jan 11, 2001
  • In strict mode the default scope is local everywhere.
  • Like in C++, for statements now form their own scopes. This affects local definitions only.
  • catch clauses now form their own scopes.
  • try statements no longer allow annotations on any of their constituent blocks.
  • Split the notion of statements into statements and directives. Directives (including most definitions) can only be at the top level of a block, while statements can be anywhere. This avoids the problem of conditional definitions as substatements of a compound statement.
  • Reorganized the statement grammar around the distinction between statements and directives. Added the Substatement nonterminal.
  • Revised the discussion of annotated blocks.
  • Revised the description of scopes.
  • Added an optional const attribute to function parameters, which makes them read-only.
Jan 9, 2001
  • Removed the attribute weak.
  • Made minor clarifications without affecting the content.
  • Added rationale for the types Object and None.
  • Added attribute-style alternative to the type syntax rationale. Feedback on this style would be appreciated.
  • Revamped variable lookup to search for instance members inside instance methods without the need for the this. prefix. Also moved the description of the lookup of static members inside a class’s scope from the class section to the variable lookup section.
  • Simplified the package referencing rules. Also fixed the examples there to not use a package as a :: qualifier, since one is not allowed there by the name lookup rules.
  • Changed explicit from a specialized attribute into a namespace similar to internal. Removed implicit. This simplified the name lookup rules.
  • Greatly simplified use’s included and excluded name possibilities to be either * or a list of identifiers.
  • Generalized the iteration protocol to allow its methods to return objects of any type as long as they have the properties 0 and 1. Stated that the values returned by the iteration protocol expire when the next iteration occurs.
  • Added rationale for not overriding !, ||, ^^, &&, and ?:.
Dec 21, 2000
Dec 20, 2000
  • Made minor wording fixes in the packages section.
  • Simplified the grammar by making the const operator have the same precedence as the other unary operators. When used as a type constructor, the const operator now applies to the array type instead of the element type. The const operator can no longer be used to emulate the const statement.
  • Simplified the IncludesExcludes grammar without changing its behavior in any significant way by merging it with the expression grammar.
Dec 18, 2000
  • Changed the syntax for defining an operator override from function operator "op" to operator function "op". operator is now an attribute instead of a keyword.
  • Renamed operator overloading to operator overriding. Moved the operators page from the libraries to the core section.
  • Removed the Boolean return type restriction from the <, <=, ==, ===, and in operators.
  • Restricted the === operator to take operands of the same class.
  • Modified single and double operator dispatch rules so that null is considered to be a member of only the types Null and Object. Without this restriction dispatch becomes ambiguous. Note that ordinary method dispatch already has this restriction built-in.
  • Specified the built-in operator definitions.
Dec 2, 2000
  • Revamped the syntax for operator overriding based on feedback from the last ECMA meeting. There is now a special syntax to do this.
  • Modified the syntax for accessing a superclass’s method to conform to the new operator overriding scheme. The super::id syntax is no longer supported. super is now an operator modifier that alters the behavior of another operator such as . (property lookup) or any of the overridable operators such as +.
  • Updated the property lookup section to accommodate the new super syntax.
Nov 29, 2000
  • Revamped property lookup. For an unqualified reference x.n, instance name lookup previously found the most derived property. Now it first looks for the least derived property and picks the most derived overload of that property. The new definition handles private property access correctly and allows property access to be optimized to a simple offset lookup when the static type of the instance is known.
  • For simplicity removed the C::n (where C is a class) case of variable lookup. Use C.n instead.
  • Added descriptions of instances, properties, and property names.
  • Stated that the activation frame of a class contains aliases to the superclass’s global members.
  • Cleaned up nomenclature: the names defined by a class are members, while the bindings inside an object are properties. Members cause properties to be constructed when a class instance is created.
Nov 20, 2000
  • Renamed the s regular expression flag from simple to span.
  • Removed the /UnitProduct production from the unit grammar.
  • Allowed white space in the unit grammar. White space can now also be used to indicate implicit multiplication there.
  • Fixed the product rule in the unit grammar.
  • Renamed expt to pow in unit expressions.
Nov 4, 2000
  • Added attributes true and false.
  • Replaced the qualified attribute by the include and exclude syntax in import and use namespace statements. Added nonreserved words include and exclude.
  • Imported definitions appear in the global scope instead of the scope of the import statement; however, the implicit use still applies to scope of the import statement.
  • Predefined type names are constants in the global scope instead of a scope enclosing the global scope.
  • Defined activation frames and qualified names.
  • Blocks with attributes are not scopes.
  • Eliminated the notion of the static and dynamic extent of a definition. Rewrote and corrected the definition extent and name lookup rules in terms of scopes and activation frames only.
  • Simplified name lookup rules, making them independent of the order in which namespaces are used.
  • Relaxed the compile-time constant rules to permit expressions that either return a known value or signal an error. Without this change packages and namespaces cannot work.
  • A class can be used instead of a namespace before the :: during name lookup. This limits the search to members of that class or its superclasses.
  • Eliminated the a::b::c syntactic sugar. In the rare cases where it’s still needed, use (a.b)::c.
  • Defining b::n when there is already a definition a::n in the same scope causes an error if both namespaces a and b are used at the point of the definition of b::n.
  • Renamed scope to regional. scope blocks can now be either local or regional blocks. Defined the notion of a regional scope.
  • Changed unit lookup to look in the Unit class instead of prefixing unit_ to each unit name and looking for a global symbol. Added the unit attribute.
  • In strict mode the default scope is local only inside functions.
  • Strict mode is optional.
Oct 27, 2000
  • Expanded the qualified attribute to optionally take a list of symbols to exclude.
  • Definitions of top-level entities in a package can only be placed in the namespace public or in namespaces defined within that package.
  • Extensively modified and updated the description of units. Allowed multiple unit on the same expression in order to support combining number class units (such as "decimal") with units of measure. Defined the grammar and semantics of unit expressions.
Oct 10, 2000
  • Removed the primitive attribute.
  • Defined methods for overriding the for-in operator.
  • Added the s (simple) flag to regular expressions. This flag makes . match every character.
Oct 9, 2000
  • Made classof, eval, and include no longer be reserved words.
  • Combined the import statement with an automatic use namespace of selected namespaces.
  • Changed classof x to x.class.
  • Fixed minor editing errors.
Sep 23, 2000
  • Defined coercions of undefined to any type except None.
  • Renamed the none and void types to None and Void. Now all predefined type names start with upper case letters.
  • Resurrected the ECMAScript Edition 3 void operator and added the classof operator.
  • Made default constructors accept named arguments.
  • Modified the syntax of class extensions and moved their description to the definitions page.
  • Changed the meaning of a::b::c to be (a.b)::c rather than the intersection of the a::c and b::c sets. This means that an identifier may be qualified by only one namespace.
  • Updated the compatibility, namespace, operator overloading, and versioning pages.
Sep 22, 2000
Sep 21, 2000
  • Updated list of reserved words.
  • Updated concept page.
  • Renamed volatile to virtual, keeping volatile’s old semantics.
  • Revamped list and descriptions of attributes. Added a number of new attributes.
  • Disallowed qualifiers in names of literal object fields and names of arguments. Allowed parenthesized indirect expressions that evaluate to strings in those cases.
  • Added const operator, array types, and const array types.
  • Changed the handling of the rest parameter to only accept named arguments if it’s preceded by a |.
Sep 18, 2000 Integrated many changes based on recent discussions with Herman and others:
  • Renamed any to Object.
  • Added notion of live and dead statements.
  • Required attributes to be compile-time constants.
  • Reinstated parenthesized expressions before :: (except in attributes).
  • Changed package names in package definitions to be identifiers or dotted identifier lists. This required renaming the package attribute to internal in order to keep the grammar LR(1)-parsable.
  • Renamed import to qualified import and use import to import.
  • Allowed super.foo as an abbreviation for this.super::foo.
  • Removed obj.(expr) member access syntax.
  • Removed definitions that define qualified identifiers.
  • Required const declarations to have initializers.
  • Each block is its own scope in strict mode.
  • Specified the means of defining parameters that take named arguments. Rewrote description of argument passing.
  • Cleaned up significant portions of the grammar.
Aug 21, 2000
  • Significantly reworked syntax of attributes and expressions to allow attributes to take arguments. This required making constructor, namespace, and use into reserved words.
  • Required parentheses after eval.
  • Removed the syntaxes for placing a parenthesized expression before :: and for unquoted package names due to grammar conflicts.
  • Removed the attribute keyword; attributes are now defined using the const syntax.
  • Removed compile blocks.
  • Added the \Q escape, which turns into nothing and is useful for using identifiers that would otherwise be reserved words.
Aug 17, 2000 First version; split off from the JavaScript 2.0 proposal.

ECMAScript 4 Netscape Proposal
Introduction
previousupnext

Wednesday, September 4, 2002


ECMAScript 4 is the next major step in the evolution of the ECMAScript language. ECMAScript 4 incorporates the following features in addition to those already found in ECMAScript 3:

  • Class definition syntax, both static and dynamic
  • Packages, including a namespace and versioning mechanism
  • Types for program and interface documentation
  • Invariant declarations such as const and final
  • private, internal, public, and user-defined access controls
  • Machine types such as int for more faithful communication with other programming languages

These facilities reinforce each other while remaining fairly small and simple. Unlike in Java, the philosophy behind them is to provide the minimal necessary facilities that other parties can use to write packages that specialize the language for particular domains rather than define these packages as part of the language core.

The versioning and access control mechanisms make the language is suitable for programming-in-the-large.


ECMAScript 4 Netscape Proposal
Introduction
Motivation
previousupnext

Wednesday, September 4, 2002

Goals

The main goals of ECMAScript 4 are:

  • Making the language suitable for writing modular and object-oriented applications
  • Making it possible and easy to write robust and secure applications
  • Improving upon ECMAScript’s facilities for interfacing with a variety of other languages and environments
  • Improving ECMAScript’s suitability for writing applications for which performance matters
  • Simplifying the language where possible
  • Keeping the language implementation compact and flexible

The following are specifically not goals of ECMAScript 4:

  • Making ECMAScript 4 suitable for all programming tasks
  • Making ECMAScript 4 similar to any existing programming language

ECMAScript is not currently an all-purpose programming language. Its strengths are its quick execution from source (thus enabling it to be distributed in web pages in source form), its dynamism, and its interfaces to Java and other environments. ECMAScript 4 is intended to improve upon these strengths, while adding others such as the abilities to reliably compose ECMAScript programs out of components and libraries and to write object-oriented programs. On the other hand, it is not our intent to have ECMAScript 4 supplant languages such as C++ and Java, which will still be more suitable for writing many kinds of applications, including very large, performance-critical, and low-level ones.

Rationale

The proposed features are derived from the goals above. Consider, for example, the goals of writing modular and robust applications.

To achieve modularity we would like some kind of a library mechanism. The proposed package mechanism serves this purpose, but by itself it would not be enough. Unlike existing ECMAScript programs which tend to be monolithic, packages and their clients are often written by different people at different times. Once we introduce packages, we encounter the problems of the author of a package not having access to all of its clients, or the author of a client not having access to all versions of the library it needs. If we add packages to the language without solving these problems, we will never be able to achieve robustness, so we must address these problems by creating facilities for defining abstractions between packages and clients.

To create these abstractions we make the language more disciplined by adding optional types and type-checking. We also introduce a coherent and disciplined syntax for defining classes and hierarchies and versioning of classes. Unlike ECMAScript 3, the author of a class can guarantee invariants concerning its instances and can control access to its instances, making the package author’s job tractable. The class syntax is also much more self-documenting than in ECMAScript 3, making it easier to understand and use ECMAScript 4 code. Defining subclasses is easy in ECMAScript 4, while doing it robustly in ECMAScript 3 is quite difficult.

To make packages work we need to make the language more robust in other areas as well. It would not be good if one package redefined Object.toString or added methods to the Array prototype and thereby corrupted another package. We can simplify the language by eliminating many idioms like these (except when running legacy programs, which would not use packages) and provide better alternatives instead. This has the added advantage of speeding up the language’s implementation by eliminating thread synchronization points. Making the standard packages robust can also significantly reduce the memory requirements and improve speed on servers by allowing packages to be shared among many different requests rather than having to start with a clean set of packages for each request because some other request might have modified some property.

ECMAScript 4 should interface with other languages even better than ECMAScript 3 does. If the goal of integration is achieved, the user of an abstraction should not have to care much about whether the abstraction is written in ECMAScript, Java, or another language. It should also be possible to make ECMAScript abstractions that appear native to Java or other language users.

In order to achieve seamless interfacing with other languages, ECMAScript should provide equivalents for the fundamental data types of those languages. Details such as syntax do not have to be the same, but the concepts should be there. ECMAScript 3 lacks support for integers, making it hard to interface with a Java method that expects a long.

ECMAScript is appearing in a number of different application domains, many of which are evolving. Rather than support all of these domains in the core ECMAScript, ECMAScript 4 should provide flexible facilities that allow these application domains to define their own, evolving standards that are convenient to use without requiring continuous changes to the core of ECMAScript. ECMAScript 4 partially addresses this goal by letting user programs define facilities such as getters and setters — facilities that could only be done by the core of the language in ECMAScript 3.


ECMAScript 4 Netscape Proposal
Introduction
Notation
previousupnext

Wednesday, June 11, 2003

Character Notation

This proposal uses the following conventions to denote literal characters:

Printable ASCII literal characters (values 20 through 7E hexadecimal) are in a blue monospaced font. Unicode characters in the Basic Multilingual Plane (code points from 0000 to FFFF hexadecimal) are denoted by enclosing their four-digit hexadecimal Unicode code points between «u and ». Supplementary Unicode characters (code points from 10000 to 10FFFF hexadecimal) are denoted by enclosing their eight-digit hexadecimal Unicode code points between «U and ». For example, the non-breakable space character would be denoted in this document as «u00A0», and the character with the code point 1234F hexadecimal would be denoted as «U0001234F». A few of the common control characters are represented by name:

Abbreviation   Unicode Value
«NUL» «u0000»
«BS» «u0008»
«TAB» «u0009»
«LF» «u000A»
«VT» «u000B»
«FF» «u000C»
«CR» «u000D»
«SP» «u0020»

A space character is denoted in this document either by a blank space where it’s obvious from the context or by «SP» where the space might be confused with some other notation.

Grammar Notation

Each LR(1) syntactic grammar and lexical grammar rule consists of a nonterminal, a , and one or more expansions of the nonterminal separated by vertical bars (|). The expansions are usually listed on separate lines but may be listed on the same line if they are short. An empty expansion is denoted as «empty».

Consider the sample rule:

SampleList 
   «empty»
|  ... Identifier
|  SampleListPrefix
|  SampleListPrefix , ... Identifier

This rule states that the nonterminal SampleList can represent one of four kinds of sequences of input tokens:

  • It can represent nothing (indicated by the «empty» alternative);
  • It can represent the token ... followed by some expansion of the nonterminal Identifier;
  • It can represent an expansion of the nonterminal SampleListPrefix;
  • It can represent an expansion of the nonterminal SampleListPrefix followed by the tokens , and ... and an expansion of the nonterminal Identifier.

Input tokens are characters (and the special End placeholder) in the lexical grammar and lexer tokens in the syntactic grammar. Input tokens to be typed literally are in a bold blue monospaced font. Spaces separate input tokens and nonterminals from each other. An input token that consists of a space character is denoted as «SP». Other non-ASCII or non-printable characters are denoted by also using « and », as described in the character notation section.

Lookahead Constraints

If the phrase “[lookahead  set]” appears in the expansion of a production, it indicates that the production may not be used if the immediately following input terminal is a member of the given set. That set can be written as a list of terminals enclosed in curly braces. For convenience, set can also be written as a nonterminal, in which case it represents the set of all terminals to which that nonterminal could expand.

For example, given the rules

DecimalDigit  0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9
DecimalDigits 
   DecimalDigit
|  DecimalDigits DecimalDigit

the rule

LookaheadExample 
   n [lookahead  {13579}] DecimalDigits
|  DecimalDigit [lookahead  {DecimalDigit}]

matches either the letter n followed by one or more decimal digits the first of which is even, or a decimal digit not followed by another decimal digit.

These lookahead constraints do not make the grammars more theoretically powerful than LR(1), but they do allow these grammars to be written more simply. The semantic engine compiles grammars with lookahead constraints into parse tables that have the same format as those produced from ordinary LR(1) or LALR(1) grammars.

Line Break Constraints

If the phrase “[no line break]” appears in the expansion of a production, it indicates that this production cannot be used if there is a line break following the last terminal matched by the grammar. Line break constraints are only present in the syntactic grammar.

Parametrized Rules

Many rules in the grammars occur in groups of analogous rules. Rather than list them individually, these groups have been summarized using the shorthand illustrated by the example below:

Metadefinitions such as

   {normalinitial}
   {allowInnoIn}

introduce grammar arguments and . If these arguments later parametrize the nonterminal on the left side of a rule, that rule is implicitly replicated into a set of rules in each of which a grammar argument is consistently substituted by one of its variants. For example, the sample rule

AssignmentExpression, 
   ConditionalExpression,
|  LeftSideExpression = AssignmentExpressionnormal,
|  LeftSideExpression CompoundAssignment AssignmentExpressionnormal,

expands into the following four rules:

AssignmentExpressionnormal,allowIn 
   ConditionalExpressionnormal,allowIn
|  LeftSideExpressionnormal = AssignmentExpressionnormal,allowIn
|  LeftSideExpressionnormal CompoundAssignment AssignmentExpressionnormal,allowIn
AssignmentExpressionnormal,noIn 
   ConditionalExpressionnormal,noIn
|  LeftSideExpressionnormal = AssignmentExpressionnormal,noIn
|  LeftSideExpressionnormal CompoundAssignment AssignmentExpressionnormal,noIn
AssignmentExpressioninitial,allowIn 
   ConditionalExpressioninitial,allowIn
|  LeftSideExpressioninitial = AssignmentExpressionnormal,allowIn
|  LeftSideExpressioninitial CompoundAssignment AssignmentExpressionnormal,allowIn
AssignmentExpressioninitial,noIn 
   ConditionalExpressioninitial,noIn
|  LeftSideExpressioninitial = AssignmentExpressionnormal,noIn
|  LeftSideExpressioninitial CompoundAssignment AssignmentExpressionnormal,noIn

AssignmentExpressionnormal,allowIn is now an unparametrized nonterminal and processed normally by the grammar.

Some of the expanded rules (such as the fourth one in the example above) may be unreachable from the grammar’s starting nonterminal; these are ignored.

Special Lexical Rules

A few lexical rules have too many expansions to be practically listed. These are specified by descriptive text instead of a list of expansions after the .

Some lexical rules contain the metaword except. These rules match any expansion that is listed before the except but that does not match any expansion after the except. All of these rules ultimately expand into single characters. For example, the rule below matches any single UnicodeCharacter except the * and / characters:

NonAsteriskOrSlash  UnicodeCharacter except * | /

ECMAScript 4 Netscape Proposal
Core Language
previousupnext

Wednesday, August 14, 2002


This chapter presents an informal description of the core language. The exact syntax and semantics are specified in the formal description. Libraries are also specified in a separate library chapter.


ECMAScript 4 Netscape Proposal
Core Language
Concepts
previousupnext

Wednesday, September 4, 2002

Values

A value is an entity that can be stored in a variable, passed to a function, or returned from a function. Sample values include:

  • undefined
  • null
  • 5 (a number)
  • true (a boolean)
  • "Kilopi" (a string)
  • [1, 5, false] (a three-element array)
  • {a:3, b:7} (an object with two properties)
  • function (x) {return x*x} (a function)
  • String (a class, a function, and a type)

Types

A type t represents three things:

  • A possibly infinite set of values S
  • A partial mapping I from the set of all values to the set S
  • A partial mapping E from the set of all values to the set S

The set S indicates which values are considered to be members of type t. We write v t to indicate that value v is a member of type t. The mapping I indicates how values may be implicitly coerced to type t. For each value v already in S, the mapping I must map v to itself. The mapping E indicates how values may be explicitly coerced to type t. For each value v in the domain of I, E must map v to the same value as I maps v. In other words, any implicit coercion is also an explicit coercion but not vice versa.

A value can be a member of multiple sets, and, in general, a value belongs to more than one type. Thus, it is generally not useful to ask about the type of a value; one may ask instead whether a value belongs to some given type. There can also exist two different types with the same set of values but different coercion mappings.

On the other hand, a variable does have a particular type. If we declare a variable x of type t, then whatever value is held in x is guaranteed to be a member of type t, and we can assign any value of type t to x. We may also be able to assign a value v t to x if type t’s mapping specifies an implicit coercion for value v; in this case the coerced value is stored in x.

Every type represents some set of values but not every set of values is represented by some type (this is required for logical consistency — there are uncountably infinitely many sets of values but only countably infinitely many types).

Every type is also itself a value and can be stored in a variable, passed to a function, or returned from a function.

Type Hierarchy

If type a’s set of values is a subset of type b’s set of values, then we say that that type a is a subtype of type b. We denote this as a b.

Subtyping is transitive, so if a b and b c, then a c. Subtyping is also reflexive: a a. Also, if v t and t s, then v s.

The set of all values is represented by the type Object, which is the supertype of all types. A variable with type Object can hold any value.

The set of no values is represented by the type Never, which is the subtype of all types. A function with the return type Never cannot return.

Classes

A class is a template for creating similar values, often called objects or instances. These instances generally share characteristics such as common methods and properties.

Every class is also a type and a value. When used as a type, a class represents the set of all possible instances of that class.

A class C can be derived from a superclass S. Class C can then inherit characteristics of class S. Every instance of C is also an instance of S, but not vice versa, which, by the definition of subtyping above, implies that C S when we consider C and S as types.

The subclass relation imposes a hierarchy relation on the set of all classes. ECMAScript 4 currently does not support multiple inheritance, although this is a possible future direction. If multiple inheritance were allowed, the subclass relation would impose a partial order on the set of all classes.

Members

A class typically contains a set of members, which can be variables, functions, etc. Members are classified as either instance or global members. Instance members become properties of instances of the class. Global members become properties (sometimes called class properties) of the class object itself; a class has only one global member with a given name.

Members can have attributes which modify their behavior.

Instances

An instance (sometimes called an object) contains a set of properties. An instance belongs to a particular class and must have properties for the instance members defined in that class and its ancestors; these bindings are made when the instance is created. An instance may also have additional dynamic properties, which can be added and deleted any time after the instance has been created.

Unless specified otherwise, each separately created instance has a distinct identity. The === operator returns false when applied to two different instances or true when applied to the same instance (with the exception of NaN, which always compares unequal to itself).

Instances provide the appearance of lasting forever, although an implementation is expected to garbage-collect them when they are no longer reachable.

Properties

A property is a runtime binding of a property name to a value. The values of some properties can change. Properties can be fixed or dynamic. Fixed properties are declared as members of a class definition and are created at the time the object is constructed. Dynamic properties can be added to an object at any time after the object was created. All ECMAScript 3 properties are dynamic.

Properties can have attributes. Fixed properties inherit their attributes from the corresponding members.

Property Names

A property name identifies a property of an instance and consists of a namespace N, an identifier id, and a class C. There is no language syntax for fully specifying a property name; in this specification property names are denoted using the notation N::idC. If the property is fixed, then C is the class in which the corresponding member is defined. If the property is dynamic, then C is the instance’s most derived class.

An instance can contain at most one property for each property name. An instance can contain multiple properties with the same namespace N and name id but different classes C; in this case the property with the most derived class C is said to override the others. The overridden properties are still present in the instance and can be accessed using the super operator.

Scopes

A scope represents one of the following delimited portions of ECMAScript source code. Some scopes are further distinguished as being regional scopes, as indicated in the table below.

Nonterminal  Regional Description
Program yes The top level of a program
PackageDefinition yes A package definition
ClassDefinition yes A class definition
FunctionDefinition yes A function definition
FunctionExpression   yes A function expression
Block no* A block (but not a group of directives prefixed with attributes)
ForStatement no* A for statement
CatchClause no* A catch clause of a try statement

*These three scopes become regional scopes if the next outer scope is a class scope.

A scope is a static entity that does not change while an ECMAScript program is running (except that if the program calls eval then new ECMAScript source code will be created which may share existing scopes or create its own scopes). A scope other than the top level of a program (the global scope) is always contained inside another scope. If two scopes overlap, one must be contained entirely within the other, so scopes form a hierarchy.

Scope information is used at run time to help with variable and property lookups and visibility checks.

A scope should not be confused with an activation frame. A scope should also not be confused with a namespace.

Activation Frames

An activation frame contains a set of runtime bindings of all qualified names defined in a scope to values. A new activation frame comes into existence each time the scope is entered. A function closure captures a reference to the activation frame in which it was created. Activation frames provide the appearance of lasting forever, although an implementation is expected to discard them after the scope is exited and any closures that captured the activation frame have been garbage-collected.

The values of some bindings in an activation frame can change. The bindings’ values begin in an uninitialized state. It is an error to read a binding in an uninitialized state.

Activation frame bindings can have attributes which modify their behavior.

Qualified Names

A qualified name consists of a namespace N and an identifier id. In this specification qualified names are denoted using the notation N::id. An activation frame can contain at most one binding for each qualified name.

Namespaces

A namespace parametrizes names. A namespace attribute N may be attached to the declaration of any name or property p. That namespace then acts like a lock on accesses to p: another piece of code may access p only by qualifying it with that namespace using N::p or by executing a use namespace(N) directive in a scope surrounding the access of p. Unlike in C++, a namespace is not a scope and does not contain any names or properties itself; a namespace only modifies the accessibility and visibility of names or properties attached to activation frames, classes, or objects.

A namespace is a value that can be passed as the first operand of the :: operator.

public is the default namespace for declarations; a use namespace(public) directive is implicit around the global scope. Each package has a predefined, anonymous internal namespace and each class has a predefined, anonymous private namespace; these provide access control. User-defined namespaces may also be used for more flexible access control.


ECMAScript 4 Netscape Proposal
Core Language
Lexer
previousupnext

Wednesday, June 4, 2003

This section presents an informal overview of the ECMAScript 4 lexer. See the stages and lexical semantics sections in the formal description chapter for the details.

Changes since ECMAScript 3

The ECMAScript 4 lexer behaves in the same way as the ECMAScript 3 lexer except for the following:

  • There are additional punctuators and reserved words.
  • The lexer recognizes several nonreserved words that have special meanings in some contexts but can be used as identifiers.
  • Only semicolon insertion on line breaks is handled by the lexer; the ECMAScript 4 parser allows semicolons to be omitted before a closing }. In addition, the ECMAScript 4 parser allows semicolons to be omitted before the else of an if-else statement and before the while of a do-while statement.
  • Semicolon insertion on line breaks are both disabled in strict mode.
  • [no line break] restrictions in grammar productions are ignored in strict mode.

Source Code

ECMAScript 4 source text consists of a sequence of UTF-16 Unicode version 2.1 or later characters normalized to Unicode Normalized Form C (canonical composition), as described in the Unicode Technical Report #15.

Comments and White Space

Comments and white space behave just like in ECMAScript 3.

Punctuators

The following ECMAScript 3 punctuation tokens are recognized in ECMAScript 4:

!   !=   !==   %   %=   &   &&   &=   (   )   *   *=   +   ++   +=   ,   -   --   -=   .   /   /=   :   ::   ;   <   <<   <<=   <=   =   ==   ===   >   >=   >>   >>=   >>>   >>>=   ?   [   ]   ^   ^=   {   |   |=   ||   }   ~

The following punctuation tokens are new in ECMAScript 4:

&&=   ...   ^^   ^^=   ||=

Keywords

The following reserved words are used in ECMAScript 4:

as   break   case   catch   class   const   continue   default   delete   do   else   export   extends   false   finally   for   function   if   import   in   instanceof   is   namespace   new   null   package   private   public   return   super   switch   this   throw   true   try   typeof   use   var   void   while   with

The following reserved words are reserved for future expansion:

abstract   debugger   enum   goto   implements   interface   native   protected   synchronized   throws   transient   volatile

The following words have special meaning in some contexts in ECMAScript 4 but are not reserved and may be used as identifiers:

get   set

Any of the above keywords may be used as an identifier by including a \_ escape anywhere within the identifier, which strips it of any keyword meanings. The two, four, and eight-digit hexadecimal escapes \xdd, \udddd, and \Udddddddd may also be used in identifiers; these strip the identifier of any keyword meanings as well.

Changes from ECMAScript 3

The following words were reserved in ECMAScript 3 but are not reserved in ECMAScript 4:

boolean   byte   char   double   final   float   int   long   short   static

The following words were not reserved in ECMAScript 3 but are reserved in ECMAScript 4:

as   is   namespace   use

Semicolon Insertion

The ECMAScript 4 syntactic grammar explicitly makes semicolons optional in the following situations:

  • Before any }
  • Before the else of an if-else statement
  • Before the while of a do-while statement (but not before the while of a while statement)
  • Before the end of the program

Semicolons are optional in these situations even if they would construct empty statements. Strict mode has no effect on semicolon insertion in the above cases.

In addition, sometimes line breaks in the input stream are turned into VirtualSemicolon tokens. Specifically, if the first through the nth tokens of an ECMAScript program form are grammatically valid but the first through the n+1st tokens are not and there is a line break (or a comment including a line break) between the nth tokens and the n+1st tokens, then the parser tries to parse the program again after inserting a VirtualSemicolon token between the nth and the n+1st tokens. This kind of VirtualSemicolon insertion does not occur in strict mode.

See also the semicolon insertion syntax rationale.

Numeric Literals

The syntax for numeric literals is the same as in ECMAScript 3, with the addition of long, ulong, and float numeric literals. The rules for numeric literals are as follows:

  • A numeric literal without a suffix is converted to an IEEE double-precision floating-point number.
  • A numeric literal with the suffix l or L is interpreted as a long value and must be a decimal or hexadecimal constant without an exponent or decimal point and be in the range of 0 through 263; furthermore, if the value is exactly 263 then the literal can only be used as the operand of the - unary negation operator.
  • A numeric literal with the suffix ul, uL, Ul, or UL is interpreted as a ulong value and must be a decimal or hexadecimal constant without an exponent or decimal point and be in the range of 0 through 264–1.
  • A numeric literal with the suffix f or F is interpreted as a float value and must be a decimal constant. Hexadecimal float constants are not permitted because the suffix would be interpreted as a hexadecimal digit.

The suffix must be adjacent to the number with no intervening white space. A number may not be followed by an identifier without intervening white space.

Regular Expression Literals

Regular expression literals begin with a slash (/) character not immediately followed by another slash (two slashes start a line comment). Like in ECMAScript 3, regular expression literals are ambiguous with the division (/) or division-assignment (/=) tokens. The lexer treats a / or /= as a division or division-assignment token if either of these tokens would be allowed by the syntactic grammar as the next token; otherwise, the lexer treats a / or /= as starting a regular expression.

This unfortunate dependence of lexical parsing on grammatical parsing is inherited from ECMAScript 3. See the regular expression syntax rationale for a discussion of the issues.


ECMAScript 4 Netscape Proposal
Core Language
Expressions
previousupnext

Monday, June 30, 2003

Most of the behavior of expressions is the same as in ECMAScript 3. Differences are highlighted below.

  {allowInnoIn}

Identifiers

Identifier 
   Identifier
|  get
|  set

The above keywords are not reserved and may be used in identifiers.

Qualified Identifiers

SimpleQualifiedIdentifier 
ExpressionQualifiedIdentifier  ParenExpression :: Identifier
QualifiedIdentifier 

Just like in ECMAScript Edition 3, an identifier evaluates to an internal data structure called a reference. However, ECMAScript 4 references can be qualified by a qualifier, which, in the general syntax, is a ParenExpression 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.

Primary Expressions

PrimaryExpression 
   null
|  true
|  false
|  Number
|  String
|  this
|  RegularExpression
ReservedNamespace 
   public
|  private
ParenExpression  ( AssignmentExpressionallowIn )
ParenListExpression 
|  ( ListExpressionallowIn , AssignmentExpressionallowIn )

public evaluates to the public namespace. private can be used only inside a class and evaluates to that class’s private namespace.

this may only be used in methods, constructors, or functions with the prototype attribute set.

Function Expressions

FunctionExpression 
   function FunctionCommon
|  function Identifier FunctionCommon

A FunctionExpression creates and returns an anonymous function.

Object Literals

ObjectLiteral  { FieldList }
FieldList 
   «empty»
NonemptyFieldList 
LiteralField  FieldName : AssignmentExpressionallowIn
FieldName 
|  String
|  Number
The ParenExpression is evaluated at run time and its result coerced to a qualified name.

Array Literals

ArrayLiteral  [ ElementList ]
ElementList 
   «empty»
|  , ElementList
LiteralElement  AssignmentExpressionallowIn

Super Expressions

SuperExpression 
   super
|  super ParenExpression

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 the left operand of a . (property lookup) or [] (indexing) operator. super 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.

Postfix Expressions

PostfixExpression 
AttributeExpression 
FullPostfixExpression 
|  PostfixExpression [no line break] ++
|  PostfixExpression [no line break] --
FullNewExpression  new FullNewSubexpression Arguments
FullNewSubexpression 
ShortNewExpression  new ShortNewSubexpression
ShortNewSubexpression 

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.

Property Operators

PropertyOperator 
Brackets 
   [ ]
|  [ ListExpressionallowIn ]
Arguments 
   ( )
ExpressionsWithRest 
RestExpression  ... AssignmentExpressionallowIn

The . operator accepts a QualifiedIdentifier as the second operand and performs a property lookup.

The grammar allows the [] operator to take multiple arguments. However, all built-in objects take at most one argument. Implementation-defined host objects may take more arguments.

For most objects other than arrays and some host objects, the expression o[m] explicitly coerces m to a qualified name q and returns the result of o.q. See property lookup.

An argument list may contain a final argument preceded by .... That argument must be an Array and cannot be null. The elements of that array become additional arguments to the function, following the arguments preceding the ..., if any; the array itself is not passed as an argument. The array must not contain holes.

Unary Operators

UnaryExpression 
|  delete PostfixExpression
|  void UnaryExpression
|  typeof UnaryExpression
|  - NegatedMinLong

The typeof operator returns a string as in ECMAScript 3. There is no way to query the most specific class of an object — all one can ask is whether an object is a member of a specific class.

Multiplicative Operators

MultiplicativeExpression 

Additive Operators

AdditiveExpression 

Bitwise Shift Operators

ShiftExpression 

Relational Operators

RelationalExpressionallowIn 
|  RelationalExpressionallowIn instanceof ShiftExpression
RelationalExpressionnoIn 

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 behaves in the same way as in ECMAScript 3 — a instanceof b follows a’s prototype chain.

Equality Operators

EqualityExpression 
   RelationalExpression
|  EqualityExpression == RelationalExpression
|  EqualityExpression != RelationalExpression
|  EqualityExpression === RelationalExpression
|  EqualityExpression !== RelationalExpression

Binary Bitwise Operators

BitwiseAndExpression 
   EqualityExpression
|  BitwiseAndExpression & EqualityExpression
BitwiseXorExpression 
   BitwiseAndExpression
|  BitwiseXorExpression ^ BitwiseAndExpression
BitwiseOrExpression 
   BitwiseXorExpression
|  BitwiseOrExpression | BitwiseXorExpression

Binary Logical Operators

LogicalAndExpression 
   BitwiseOrExpression
|  LogicalAndExpression && BitwiseOrExpression
LogicalXorExpression 
   LogicalAndExpression
|  LogicalXorExpression ^^ LogicalAndExpression

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.

LogicalOrExpression 
   LogicalXorExpression
|  LogicalOrExpression || LogicalXorExpression

Conditional Operator

ConditionalExpression 
   LogicalOrExpression
|  LogicalOrExpression ? AssignmentExpression : AssignmentExpression
NonAssignmentExpression 
   LogicalOrExpression
|  LogicalOrExpression ? NonAssignmentExpression : NonAssignmentExpression

Assignment Operators

AssignmentExpression 
   ConditionalExpression
|  PostfixExpression = AssignmentExpression
CompoundAssignment 
   *=
|  /=
|  %=
|  +=
|  -=
|  <<=
|  >>=
|  >>>=
|  &=
|  ^=
|  |=
LogicalAssignment 
   &&=
|  ^^=
|  ||=

Comma Expressions

ListExpression 
   AssignmentExpression
|  ListExpression , AssignmentExpression

Type Expressions

TypeExpression  NonAssignmentExpression

Compile-Time Constant Expressions

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.
  • Uses of the operators + (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.
  • References to compile-time constants, subject to the restrictions below. The references may be qualified, but the qualifiers themselves have to be compile-time constant expressions.
  • Lookup of properties of compile-time expressions.
  • Calls to pure functions using compile-time expressions as arguments.

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. An ECMAScript 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.

  • D is visible to R.
  • There does not exist any scope between D’s scope and R’s scope that contains any declarations that can shadow D.
  • D was not hidden or made inaccessible by a conflict arising from another 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.

Restrictions

ECMAScript 4 imposes the following restrictions:

  • TypeExpressions must be compile-time constant expressions that evaluate to types. Except for the TypeExpression specifying the superclass of a class, these expressions can contain forward references.
  • Attributes must be compile-time constant expressions that evaluate to attributes. These expressions cannot contain forward references when used as attributes of statements or directives.
  • const definitions defining constants that are used in compile-time constant expressions must have initializers that are themselves compile-time constant expressions. These initializers cannot contain forward references.
  • Default parameter values must be compile-time constant expressions. These expressions can contain forward references.
  • Initializers for instance members must be compile-time constant expressions. These expressions can contain forward references.
  • Each live import, class, 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.
  • Each live declaration of a class member must dominate the end of the class definition.

A statement A dominates statement B if any of the following conditions are met:

  • A and B are the same statement.
  • A and B are in the same block, with A before B and no case or default labels between them.
  • Statement B is enclosed inside statement C and A dominates C.
  • Statement A is enclosed inside a block C, C is not prefixed by an attribute that evaluates to 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:

  • A is prefixed by an attribute that evaluates to false.
  • There exists a 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.
  • A is enclosed inside statement B and B is dead.

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.


ECMAScript 4 Netscape Proposal
Core Language
Statements
previousupnext

Wednesday, June 4, 2003

Statements

Most of the behavior of statements is the same as in ECMAScript 3. Differences are highlighted below.

  {abbrevnoShortIffull}
Statement 
   ExpressionStatement Semicolon
|  SuperStatement Semicolon
|  Block
|  LabeledStatement
|  IfStatement
|  DoStatement Semicolon
|  WhileStatement
|  ForStatement
|  WithStatement
|  ContinueStatement Semicolon
|  BreakStatement Semicolon
|  ReturnStatement Semicolon
|  ThrowStatement Semicolon
Substatement 
|  Statement
|  SimpleVariableDefinition Semicolon
|  Attributes [no line break] { Substatements }
Substatements 
   «empty»
SubstatementsPrefix 
   «empty»
Semicolonabbrev 
   ;
|  VirtualSemicolon
|  «empty»
SemicolonnoShortIf 
   ;
|  VirtualSemicolon
|  «empty»
Semicolonfull 
   ;
|  VirtualSemicolon

A Substatement is a statement directly contained by one of the compound statements label:, if, switch, while, do while, for, or with (but not a block). A substatement cannot be a directive except that, in non-strict mode only, it can be a var definition without attributes or types.

A substatement can also consist of one or more attributes applied to a group of substatements enclosed in braces. The attributes must evaluate to either true or false. The braces do not form a scope in this case.

The Semicolon productions allow both grammatical and line-break semicolon insertion.

Empty Statement

EmptyStatement  ;

Expression Statement

ExpressionStatement  [lookahead{function{}] ListExpressionallowIn

Super Statement

SuperStatement  super Arguments

The super statement calls the superclass’s constructor. It can only be used inside a class’s constructor.

Block Statement

Block  { Directives }

A block groups statements and forms a scope.

Labeled Statements

LabeledStatement  Identifier : Substatement

If Statement

IfStatementabbrev 
|  if ParenListExpression SubstatementnoShortIf else Substatementabbrev
IfStatementfull 
|  if ParenListExpression SubstatementnoShortIf else Substatementfull
IfStatementnoShortIf  if ParenListExpression SubstatementnoShortIf else SubstatementnoShortIf

The semicolon is optional before the else.

Switch Statement

SwitchStatement  switch ParenListExpression { CaseElements }
CaseElements 
   «empty»
CaseElementsPrefix 
   «empty»
CaseElement 
   Directive
CaseLabel 
   case ListExpressionallowIn :
|  default :

Do-While Statement

DoStatement  do Substatementabbrev while ParenListExpression

The semicolon is optional before the closing while.

While Statement

WhileStatement  while ParenListExpression Substatement

For Statements

ForStatement 
   for ( ForInitializer ; OptionalExpression ; OptionalExpression ) Substatement
|  for ( ForInBinding in ListExpressionallowIn ) Substatement
ForInitializer 
   «empty»
|  Attributes [no line break] VariableDefinitionnoIn
ForInBinding 
|  Attributes [no line break] VariableDefinitionKind VariableBindingnoIn
OptionalExpression 
   ListExpressionallowIn
|  «empty»

A for statement forms a scope. Any definitions in it (including the ForInitializer and ForInBinding) are visible inside the for statement and its substatement, but not outside the for statement. However, a var definition inside a for statement may be hoisted to the nearest enclosing regional scope.

With Statement

WithStatement  with ParenListExpression Substatement

Continue and Break Statements

ContinueStatement 
   continue
|  continue [no line break] Identifier
BreakStatement 
   break
|  break [no line break] Identifier

Return Statement

ReturnStatement 
   return
|  return [no line break] ListExpressionallowIn

A return statement can only be used inside a function or constructor. The return statement cannot have an expression if used inside a constructor or a setter.

Throw Statement

ThrowStatement  throw [no line break] ListExpressionallowIn

Try Statement

TryStatement 
   try Block CatchClauses
|  try Block CatchClausesOpt finally Block
CatchClausesOpt 
   «empty»
CatchClauses 
CatchClause  catch ( Parameter ) Block

Each CatchClause forms a scope. The Parameter, if any, is defined as a local variable visible only within the CatchClause.

The Blocks following try and finally are also scopes like other Block statements.

Directives

Directive 
|  Statement
|  AnnotatableDirective
|  Attributes [no line break] AnnotatableDirective
|  Attributes [no line break] { Directives }
|  Pragma Semicolon
AnnotatableDirective 
   VariableDefinitionallowIn Semicolon
|  NamespaceDefinition Semicolon
|  ImportDirective Semicolon
|  UseDirective Semicolon
Directives 
   «empty»
DirectivesPrefix 
   «empty»

Attributes can be applied to a group of directives by following them by a {, the directives, and a }. The attributes apply to all of the enclosed directives. The attribute true is ignored. The attribute false causes all of the enclosed directives to be omitted. When used this way, the braces do not form a block or a scope.

Annotated groups are useful to define several items without having to repeat attributes for each one. For example,

class foo {
  var z:Integer;
  public var a;
  private var b;
  private function f() {}
  private function g(x:Integer):Boolean {}
}

is equivalent to:

class foo {
  var z:Integer;
  public var a;
  private {
    var b;
    function f() {}
    function g(x:Integer):Boolean {}
  }
}

Programs

Program 

ECMAScript 4 Netscape Proposal
Core Language
Definitions
previousupnext

Thursday, May 22, 2003

Introduction

Definitions are directives that introduce new constants, variables, functions, classes, namespaces, and packages. All definitions except those of packages can be preceded by zero or more attributes. In non-strict mode there must not be any line breaks between the attributes or after the last attribute.

Attributes

Attributes 
   Attribute
AttributeCombination  Attribute [no line break] Attributes
Attribute 
|  true
|  false

An attribute is an expression (usually just an identifier) that modifies a definition’s meaning. Attributes can specify a definition’s scope, namespace, semantics, and other hints. An ECMAScript program may also define and subsequently use its own attributes. Attributes can be qualified identifiers (as long as they don’t start with a () and dotted and function call expressions, but they must be compile-time constants.

The table below summarizes the predefined attributes.

Category Attributes Behavior
Namespace private
internal
public
Makes the definition visible only in the enclosing class’s private namespace (private), the enclosing package’s private namespace (internal), or anywhere (public).
Visibility Modifier enumerable This definition can be seen using a for-in statement.
explicit This top-level definition is not shared via an import directive.
Class Modifier final This class cannot be subclassed. Can be used only on classes.
dynamic Direct instances of this class can contain dynamic properties. Can be used only on classes.
Member Modifier   static
virtual
final
The definition creates a global member (static) or instance member (virtual or final) of the enclosing class. If defining an instance member, the definition can (virtual) or cannot (final) be overridden in subclasses. Can be used only on class members.
override
override(true)
override(false)
override(undefined) 
Assertion that the definition overrides (override or override(true)), may override (override(undefined)), or does not override (override(false)) a member of a superclass. Can be used only on class members. Controls errors only.
Conditional true
false
The definition or directive is (true) or is not (false) processed.
Miscellaneous prototype Allows a function to access this and be used as a prototype-based constructor.
unused Assertion that the definition is not used.

Multiple conflicting attributes cannot be used in the same definition, so virtual final private is an error. The attributes true and false do not conflict. Specifying an attribute more than once has the same effect as specifying it once.

Namespace Attributes

Namespace attributes control the definition’s visibility. User-defined attributes provide a finer grain of visibility control.

Every package P has a predefined, anonymous namespace PackageInternalP. That namespace is attached to all definitions with the internal attribute in that package. Package P’s scope includes an implicit use namespace(PackageInternalP) definition around the package that grants access to these definitions from within the package only.

Every class C has a predefined, anonymous namespace ClassInternalC. That namespace is attached to all definitions with the private attribute in that class. Class C’s scope includes an implicit use namespace(ClassInternalC) definition around the class that grants access to these definitions from within that class only. private can only be used inside a class.

Namespace attributes, including user-defined namespaces, are additive; if several are given for a definition, then that definition is put into each of the designated namespaces. Thus, a single definition may define a name in two or more namespaces namespace1 and namespace2 by listing the namespaces as attributes: namespace1 namespace2 var x. Such multiple definitions are aliases of each other; there is only one storage location x.

A definition of a name id is always put into the namespaces explicitly specified in the definition’s attributes. In addition, the definition may be placed in additional namespaces according to the rules below:

  • If the definition explicitly specifies one or more namespaces N:
    • If the definition defines an instance member in a class C and one of C’s ancestors contains an instance member M with name id and at least one of the namespaces in N, then the definition will override M and be placed in all of M’s namespaces. It is an error if N contains another namespace that is not in M’s namespaces.
    • Otherwise, the definition is put into each of the namespaces in N; however, to avoid confusion the override(false) or override(undefined) attribute is required on the definition if an inherited instance member with the name id is visible at the point of the definition. This restriction prevents one from accidentally defining a private instance member with the same name as a public instance member in a superclass (by the rules of member lookup, such a private member would be shadowed by the existing public member for all unqualified accesses).
  • If the definition does not explicitly specify any namespaces:
    • If the definition is in a class C and one of C’s ancestors contains a visible member M with name id, then the definition will override M and be placed in all of M’s namespaces. It is an error to attempt to override two different members with one definition.
    • Otherwise, the definition is put into the public namespace.

Visibility Modifier Attributes

Visibility modifier attributes control the definition’s visibility in several special cases.

enumerable

An enumerable definition can be seen by the for-in iteration statement. A non-enumerable definition cannot be seen by such a statement. enumerable only applies to public definitions.

The default for dynamic properties and class properties is enumerable. The default for instance properties is non-enumerable. There is no way to make a user-defined dynamic or class property non-enumerable.

explicit

explicit is used to add definitions to a package P without having them conflict with definitions in other packages that import package P. explicit prevents the definition from being accessed as a top-level variable when a package is imported. The definition can still be accessed as a property of an object. For example,

package My.P1 {
  const c1 = 5;
  explicit const c2 = 7;
}

package My.P2 {
  import P = My.P1;  // Imports My.P1 without qualification
  c1;                // OK; evaluates to 5
  c2;                // Error: c2 not defined because explicit variables are not shared
  P.c2;              // OK: explicit properties are visible
}

Class Modifier Attributes

Class modifier attributes apply to the definition of a class C itself. They may only be used on definitions of classes.

final

If a class C is defined using the final attribute, then any attempt to define a subclass of C signals an error.

Note that final is also a member modifier — when used on a class member, final makes that member nonoverridable. If final is used on a class member that is itself a class, then it acts like a class modifier instead of a member modifier — it prevents the inner class from being subclassed.

dynamic

Direct instances of a dynamic class C can contain dynamic properties. Other instances cannot contain dynamic properties. A class is dynamic if it has the dynamic attribute or it has a dynamic ancestor other than Object.

Member Modifier Attributes

Member modifier attributes modify a class member definition’s semantics with respect to a class hierarchy. They may only be used on a definition of a member M of a class C. They cannot be used on definitions that, for example, create local variables inside a function.

static, virtual, and final

The static attribute makes M be a global member of C.

The virtual and final attributes make M be an instance member of C.

The final attribute prevents subclasses from defining their own members with the name M (unless they can’t see this M, in which case they can define an independent M). virtual allows subclasses to override M.

The default setting for the definition of a member M of a class named C is:

Default Attribute Kind of Member M
none — the function is treated specially as a class constructor   function C
virtual function F where the name F differs from C
final var and const definitions
none — static attribute must be specified explicitly class and namespace definitions

These attributes may not be used on an export definition, since export reuses the original member’s setting.

Note that final is also a class modifier — when used on a class member M, final prevents M from being subclassed rather than making M be a nonoverridable instance member of C.

override

The override attribute reports errors; it has no other effect on the behavior of the program. The override attribute can only be used on definitions in a class and describes the programmer’s intent to either override or not override a member from a superclass. If the actual behavior, as defined by the namespace defaulting and overriding rules, differs, then an error is signaled.

The table below describes the behavior of when a definition of a member M with name id is placed in a class C:

Override attribute given
None override or
override(true)
override(undefined) override(false)
M overrides a member in some superclass according to the namespace defaulting and overriding rules Error OK OK Error
M does not override anything but there exists an ancestor of C with a member with name id visible at the point of definition of M Error Error OK OK
M does not override anything and no ancestor of C has a member with name id visible at the point of definition of M OK Error OK OK

The middle case arises for example when an ancestor of a class C defines a public member named X and class C attempts to define a private member named X.

Conditional Attributes

An attribute whose value is true causes the definition or directive to be evaluated normally. An attribute whose value is false causes the definition or directive to be skipped; the remaining attributes and the body of the definition or directive are not evaluated. These are useful for turning definitions on and off based on configuration settings, such as:

const debug = true;
const nondebug = !debug;

debug var nCalls = 0;
debug function checkConsistency() {...}

Miscellaneous Attributes

prototype

The prototype attribute can only be used on a function. A function with this attribute treats this in the same manner as ECMAScript 3 and defines its own prototype-based class as in ECMAScript 3. By default, the prototype attribute is set on any unchecked function. It can be set explicitly on other functions as long as they are not getters, setters, or constructors.

unused

The unused attribute is a hint that the definition is not referenced anywhere. Referencing the definition will generate an error.

User-Defined Attributes

A user-defined attribute may be defined using a const definition or other definitions that define constants. All attributes must be compile-time constants. For example:

const ipriv = internal static;
explicit namespace Version1;
explicit namespace Version2;
internal const Version1and2 = Version1 Version2;

class C {
  ipriv var x;                          // Same as internal static var x;
  Version1and2 var simple;              // Same as Version1 Version2 var simple;
  Version2 var complicated;
  ipriv const a:Array = new Array(10);

  private var i;
  for (i = 0; i != 10; i++) a[i] = i;
}

Definition Scope

A definition applies to the innermost enclosing scope except when it is hoisted. If that scope is a class, the definition appears as a member of that class. If that scope is a package, the definition appears as a member of that package.

Scope Hoisting

For compatibility with ECMAScript 3, in some cases a definition’s scope is hoisted to the innermost regional scope R instead of the innermost scope S. This happens only when all of the conditions below are met:

  • The definition is a var definition.
  • The definition does not specify a type.
  • The definition has no attributes other than true.
  • The regional scope R is not a class.
  • Strict mode is not in effect.

When a definition of n is hosted, the effect is as though n were declared (but not initialized) at the top of the regional scope R.

Definitions not meeting the above criteria are not hoisted. However, an inner non-hoisted definition of name n in scope S within regional scope R prevents n from being referenced or defined in any scope within R but outside S; see definition conflicts.

Extent

A definition extends an activation frame with one or more bindings of qualified names to values. The bindings are generally visible from the activation frame’s scope. However, a definition may be invisible or partially invisible inside its scope either because it is shadowed by a more local definition or it uses a namespace that is not used. The name lookup rules specify the detailed behavior of accessing activation frame bindings.

Each definition or declaration D of a name n applies to some scope S using the rules above. Any of S’s activation frames will contain a binding for n as soon as S is entered. That binding starts in the following state:

  • In non-strict mode, a var n definition D without a type or attributes is initialized to the value undefined upon entry into S.
  • In non-strict mode, a function n definition D without types or attributes is initialized to its closure upon entry into S instead of at the time D is executed.
  • A class definition D of a name n in scope S binds n to an opaque value V of type Type upon entry into S. V may be used as a type to declare variables, but all other operations are prohibited. V becomes the actual class object at the time the definition is executed, after which point instances of V may be created and subclasses may be derived from V.
  • All other definitions produce bindings in the uninitialized state upon entry into the scope S. The bindings are initialized at the time the definition is executed.

Accessing an activation frame binding in the uninitialized state is an error. If this happens, implementations are encouraged to throw an exception, but may return a value V if they can prove that the definition would assign the value V to the binding.

Definition Conflicts

In general, it is not legal to rebind the same name in the same namespace within an activation frame A. There are a couple exceptions:

  • Multiple var definitions (which may be hoisted) are allowed in a regional scope as long as all such definitions have no type and no attributes and strict mode is not in effect.
  • A getter and a setter with the same name may be defined independently.

In addition, if a name n is defined in a scope S inside regional scope R, then it is not permitted to access a definition of n made outside of R from anywhere inside R. Also, two nested scopes S1 and S2 located inside the same regional scope R cannot both define n (S1, S2, and R may be the same scope). In either of these situations, n may be hoisted; if hoisting is not allowed, an error occurs. For example,

const b:Integer = 1;

function f(c:Boolean):Integer {
  const a = b;  // Error: b is defined inside the local scope below, which prevents accesses to global b
                // from anywhere inside the regional scope
  if (c) {
    const b:Integer = a + 10;  // OK to hide the global b from here.
    return b;
  }
  return a;
}

function g(c:Boolean):Integer {
  const b = 3;  // OK to hide the global b from here.
  if (c) {
    const b:Integer = 10;  // Error: can’t redefine b inside the same regional scope.
    return b;
  }
  return b;
}

function h(c:Boolean):Integer {
  if (c) {
    const b:Integer = 10;  // OK to hide the global b from here.
    return b;
  } else {
    const b:Integer = 42;  // OK: Two independent local definitions of b.
    return b;
  }
}

To help catch accidental redefinitions, binding a qualified name q::n in activation frame A when there is already a binding r::n in A causes an error if both namespaces q and r are used at the point of the definition of q::n and the bindings are not aliases of each other. This prevents the same name from being used for both public and private variables in the same class. Two bindings sharing the same name but with different namespaces may still be introduced into an activation frame, but only by code that does not use one or both of the namespaces.

Examples

In the example below the comments indicate the scope and namespace of each definition:

var a0;                  // Public global variable
internal const a1 = true;// Package-visible global variable
private var a2;          // Error: private  can only be used inside a class
public var a3 = b1;      // Public global variable

if (a1) {
  var b0;                // Local to this block
  var b1;                // Hoisted to the global level because of the reference to b1 in the definition of a3
}

if (a1) {
  var b0;                // Local to this block
}

public function F() {    // Public global function
  var c0;                // Local to this function
  internal var c1;       // Local to this function  (may generate a style warning)
  public var c2;         // Local to this function  (may generate a style warning)
}

class C {                // Public global class
  var e0;                // Public class instance variable
  private var e1;        // Class-visible class instance variable
  internal var e2;       // Package-visible class instance variable
  public var e3;         // Public class instance variable
  static var e4;         // Public class-global variable
  private static var e5; // Class-visible class-global variable
  internal static var e6;// Package-visible class-global variable
  public static var e7;  // Public class-global variable

  if (a1) {
    var f0;              // Local to this block
    private var f1;      // Local to this block  (may generate a style warning)
  }
  public function I() {} // Public class method
}

Discussion

Should we have a protected Attribute? It has been omitted for now to keep the language simple, but there does not appear to be any fundamental reason why it could not be supported. If we do support it, it might be better to choose the C++ protected concept (visible only in class and subclasses); the Java protected concept (visible in class, subclasses, and the original class’s package) could be represented as internal protected.


ECMAScript 4 Netscape Proposal
Core Language
Variables
previousupnext

Wednesday, June 4, 2003

Variable Definitions

VariableDefinition  VariableDefinitionKind VariableBindingList
VariableDefinitionKind 
   var
|  const
VariableBindingList 
   VariableBinding
|  VariableBindingList , VariableBinding
VariableBinding  TypedIdentifier VariableInitialisation
VariableInitialisation 
   «empty»
|  = VariableInitializer
VariableInitializer 
   AssignmentExpression
TypedIdentifier 
|  Identifier : TypeExpression

A SimpleVariableDefinition represents the subset of VariableDefinition expansions that may be used when the variable definition is used as a Substatement instead of a Directive in non-strict mode. In strict mode variable definitions may not be used as substatements.

SimpleVariableDefinition  var UntypedVariableBindingList
UntypedVariableBindingList 
UntypedVariableBinding  Identifier VariableInitialisationallowIn

A variable defined with var can be modified, while one defined with const is read-only after its value is set. Identifier is the name of the variable and TypeExpression is its type. Identifier can be any non-reserved identifier. TypeExpression must be a compile-time expression that evaluates to a type t other than Never. TypeExpression may contain forward references to compile-time constants defined later in the program.

If provided, AssignmentExpression gives the variable’s initial value v. If AssignmentExpression is not provided in a var definition, then undefined is assumed. If the variable being defined is not an instance member of a class, then the AssignmentExpression is evaluated at the time the variable definition is evaluated. The resulting value is then implicitly coerced to the variable’s type t and stored in the variable. If the variable is defined using var, any values subsequently assigned to the variable are also implicitly coerced to type t at the time of each such assignment. If the variable is an instance member of a class, then the AssignmentExpression is evaluated each time an instance of the class is constructed.

Reading or writing a variable before its definition is evaluated signals an error except when the variable definition has no attributes and no type and strict mode is not in effect; in that case, the variable may be read or written prior to its definition being evaluated and its initial value is undefined.

Multiple variables separated by commas can be defined in the same VariableDefinition. The values of earlier variables are available in the AssignmentExpressions of later variables.

If omitted, TypeExpression defaults to type Object. Thus, the definition

var a, b=3, c:Integer=7, d, e:Integer, f:Number=c;

is equivalent to:

var a:Object = undefined;
var b:Object = 3;
var c:Integer = 7;
var d:Object = undefined;
var e:Integer = undefined;   // Implicitly coerced to NaN
var f:Number = c;            // 7

The compiler might issue a warning for a VariableDefinition that contains an untyped variable prior to a typed variable to remind programmers that the type of d is Object rather than Integer in var d, e:Integer.

Constant Definitions

const means that assignments to Identifier are not allowed after the constant has been set. Once defined, a constant cannot be redefined in the same scope, even if the redefinition would be to the same value. If the VariableBinding in a const declaration does not contain an initializer, then the constant may be written once after it is defined. Any attempt to read the constant prior to writing its value will result in an error. For example:

function f(x) {return x+c}

f(3);           // Error: c’s value is not defined
const c = 5;
f(3);           // Returns 8
const c = 5;    // Error: redefining c

Just like any other definition, a constant may be rebound after leaving its scope. For example, the following is legal; j is local to the block, so a new j binding is created each time through the loop:

var k = 0;
for (var i = 0; i < 10; i++) {
  const j = i;
  k += j;
}

A const definition defines a compile-time constant if it has an initializer and that initializer is a compile-time constant expression which may contain forward references to other compile-time constants.

In order for the compiler to be able to distinguish const definitions that define run-time constants from ones that define compile-time constants, it must be able to resolve each variable referenced in a const initializer to a scope. Because of this, a const initializer may only refer to variables declared at compile time; referring to a dynamically created variable not declared at compile time results in a compile-time error. It is also an error to attempt to create a dynamic variable that changes the resolution of a variable in a const initializer. For example:

const a = 5;      // OK: a is a compile-time constant with the value 5
const b = a + c;  // 
OK: b is a compile-time constant with the value 7
const c = 2;      // 
OK: c is a compile-time constant with the value 2
const d = e;      // 
OK: d is a run-time constant that starts as undefined
var e = 2.718281828459045;
f = "Run time";
const g = f;      // 
Error: f is not declared at compile time
const h = uint;   // 
OK: h is a compile time constant that holds the type uint
this.ulong = 15;  // 
OK: creates the global property ulong that shadows the system ulong type
this.uint = 15;   // 
Error: can’t create the global property uint because it would
                  // 
  change the resolution of uint in the definition of h

Inside a class, const preceded by final defines an instance member. Preceding it with virtual would also define an instance member, but is only useful if one wants subclasses to be able to override the constant. Precede it with static to define a global member. The default is final.

If const is declaring an instance member m of a class, then the initializer is evaluated each time an instance of the class is constructed. If absent, then the member’s property may be written exactly once, cannot be re-written after it has been written, and must be written before it can be read. For example:

class C {
  static const red = 0xFF0000;      // Defines static constant C.red
  static const green = 0x00FF00;    // Defines static constant C.green
  static const blue = 0x0000FF;     // Defines static constant C.blue
  static const infrared;            // Defines uninitialized static constant C.infrared

  const myColor;                    // Defines instance constant C::myColor with value set by the constructor
  final const yourColor;            // Defines instance constant C::yourColor with value set by the constructor
  const ourColor = 0;               // Defines instance constant C::ourColor that is always zero (not very useful)
  virtual const theirColor = 0;     // Defines instance constant C::theirColor that can be overridden by subclasses

  function C(x:int) {
    myColor = x;                    // Sets this instance’s myColor
    ourColor = x;                   // Error: ourColor is already set to 0
    myColor = x;                    // Error: myColor can be set only once
    var a = [x, this];
    a[1].yourColor = x;             // Sets this instance’s yourColor
  }
}

Getters and Setters

A definition var x:t = v internally creates a hidden variable and defines a getter and a setter to access that variable:

  • Evaluate t, which should evaluate to a type.
  • Create an anonymous variable .
  • Implicitly coerce undefined to type t (such a coercion must exist for every type) and assign the result to .
  • Define a getter function get x():t {return }.
  • Define a setter function set x(a:t):Void { = a}.
  • Evaluate v, implicitly coerce it to type t, and assign the result to .

A definition const x:t = v internally creates a hidden variable and defines a getter to access that variable:

  • Evaluate t, which should evaluate to a type.
  • Create an anonymous variable .
  • Define a getter function get x():t {return }.
  • Define a setter function set x(a:t):Never {throw ConstWriteError}.
  • Evaluate v, implicitly coerce it to type t, and assign the result to .

This relationship between a variable and its getter and setter is normally transparent but can be exploited occasionally. For instance, a variable can be declared that is private for writing but public for reading:

private var name:String;
public export get name;

A subclass may override a variable’s getter or setter. To do this, the original variable has to be declared non-final because variables are final by default:

class C {
  virtual var x:Integer;
  var y:Integer;
}

class D extends C {
  override function set x(a:Integer):Integer {y = a*2}
}

var c = new C;
c.x = 5;
c.x;       // Returns 5
c.y;       // Returns NaN (the default value for an Integer variable)
var d = new D;
d.x = 5;
d.x;       // Returns NaN
d.y;       // Returns 10

ECMAScript 4 Netscape Proposal
Core Language
Functions
previousupnext

Monday, April 28, 2003

Syntax

FunctionDefinition  function FunctionName FunctionCommon
FunctionName 
|  get [no line break] Identifier
|  set [no line break] Identifier
FunctionCommon  ( Parameters ) Result Block

Like other definitions, a function definition may be preceded by one or more attributes, which affect the function’s scope, namespace, and semantics. Every function (except a getter or a setter) is also a value and has type Function.

Unless a function f is defined with the prototype attribute (either explicitly or by default because f is unchecked), that function does not define a class, f’s name cannot be used in a new expression, and f cannot refer to this unless f is an instance method or constructor of a class.

A FunctionDefinition can specify a function, getter (if its name is preceded by get), or setter (if its name is preceded by set).

Parameters give the names and the types of the function’s parameters. Result gives the type of the function’s result. The Block contains the function body and is evaluated only when the function is called.

Parameter Declarations

A function may take zero or more parameters and an optional rest parameter. Optional parameters may follow but not precede required parameters (this condition is not in the grammar but is checked by the formal semantics).

Parameters 
   «empty»
NonemptyParameters 

Individual parameters have the forms:

Parameter  ParameterAttributes TypedIdentifierallowIn
ParameterAttributes 
   «empty»
|  const
ParameterInit 
   Parameter
RestParameter 
   ...

The TypeExpression gives the parameter’s type and defaults to type Object. The TypeExpression must evaluate to a type other than Never.

If a Parameter is followed by a =, then that parameter is optional. If a function call does not provide an argument for an optional parameter, then that parameter is set to the value of its AssignmentExpression, implicitly coerced to the parameter’s type if necessary. The AssignmentExpression must be a compile-time constant.

If a Parameter is prefixed with const, then the parameter is declared using const instead of var. The effect is that the parameter’s value cannot be changed from within the function. Without the const, the function can change the parameter’s value, which, however, has no effect on the argument.

If a function call does not provide an argument for a required Parameter, then an error occurs unless the function is unchecked, in which case the parameter gets the value undefined, implicitly coerced to the parameter’s type if necessary.

The parameters’ Identifiers are local variables with types given by the corresponding TypeExpressions inside the function’s Block. Code in the Block may read and write these variables. Arguments are passed by value, so writes to these variables do not affect the passed arguments’ values in the caller.

Attempting to define a function with two different parameters with the same name is an error.

Rest Parameter

If the ... is present, the function accepts arguments not matched by any of the other listed parameters. If a parameter is given after the ..., then that parameter’s identifier is bound to an array of all remaining arguments. That identifier is declared as a local var or const using the type Array. The remaining arguments are stored as elements of the rest array with numeric indices starting from 0.

Each unchecked function also has a predefined const arguments local variable which holds an array (of type Array) of all arguments passed to this function.

Result Type

Result 
   «empty»
|  : TypeExpressionallowIn

The function’s result type is TypeExpression, which defaults to type Object if not given. The TypeExpression must evaluate to a type.

If the function does not return a useful value, it’s good practice to set TypeExpression to Void to document this fact. If the function cannot return at all (it either always falls into an infinite loop or throws an exception), then it’s good practice to set TypeExpression to Never to document this fact; this also lets the compiler know that code after a call to this function is unreachable, which can help cut down on spurious warnings.

Evaluation Order

A function’s parameter and result TypeExpressions are evaluated at the time the function definition or declaration is executed. These types are then saved for use in argument and result coercions at the time the function is called.

The static and dynamic extent of a parameter includes all subsequent parameters’ and the result type’s TypeExpressions and AssignmentExpressions. However, the case where a subsequent parameter’s or the result type’s TypeExpression or AssignmentExpression references a prior parameter is reserved for a future language extension. For now, an implementation should raise an error in this case:

const t = Integer;
function choice(a:Boolean, t:Type, c:t, d:t):t {
  return a ? c : d;
}

This definition of choice should (for now) be an error and not:

function choice(a:Boolean, t:Type, c:Integer, d:Integer):Integer {
  return a ? c : d;
}

The intent is that a future language extension might make the first definition of choice legal and permit calls to it like choice(true,String,"Be","Not Be"), which would return "Be".

When a function is called, the following list indicates the order of evaluation of the various expressions in a FunctionDefinition. These steps are taken only after all of the argument names and values have been evaluated.

  1. If the function is unchecked, bind the arguments local variable to an array of all arguments and their names.
  2. Get the saved type t that was the result of evaluating the first parameter’s TypeExpression at the time the function was defined.
  3. If the first parameter is required and no argument has been supplied for it, then raise an error unless the function is unchecked, in which case let undefined be the first parameter’s value.
  4. If the first parameter is optional and there is an argument remaining, use the value of the argument. If there are no remaining arguments, then evaluate the first parameter’s AssignmentExpression and let it be the first parameter’s value.
  5. Implicitly coerce the argument (or default) value to type t and bind the parameter’s Identifier to the result.
  6. Repeat steps 2-5 for each additional parameter.
  7. If there is a RestParameter with an Identifier, bind that Identifier to an array of the remaining arguments using indices starting from 0.
  8. If there is no RestParameter and any arguments remain, raise an error unless the function is unchecked.
  9. Evaluate the body.
  10. Get the saved type r that was the result of evaluating the result TypeExpression at the time the function was defined.
  11. Implicitly coerce the result to type r and return it.

Getters and Setters

If a FunctionName contains the keyword get or set, then the defined function is a getter or a setter.

A getter must not take any parameters. Unlike an ordinary function, a getter is invoked by merely mentioning its name without an Arguments list in any expression except as the destination of an assignment. For example, the following code returns the string “<2,3,1>”:

var x:Integer = 0;
function get serialNumber():Integer {return ++x}

var y = serialNumber;
return "<" + serialNumber + "," + serialNumber + "," + y + ">";

A getter must either evaluate a return statement or throw an exception; it cannot fall off the end without returning a value.

A setter must take exactly one required parameter. Unlike an ordinary function, a setter is invoked by merely mentioning its name (without an Arguments list) on the left side of an assignment or as the target of a mutator such as ++ or --. The setter should not return a value and should be declared as returning type Void or Never. The result of an assignment expression is the argument passed to the setter. For example, the following code returns the string “<1,2,42,43>”:

var x:Integer = 0;
function get serialNumber():Integer {return ++x}
function set serialNumber(n:Integer):Void {x=n}

var s = "<" + serialNumber + "," + serialNumber;
s += "," + (serialNumber = 42);
return s + "," + serialNumber + ">";

A setter cannot return a value; it may invoke a return statement as long as that statement does not supply an expression.

A setter can have the same name as a getter in the same lexical scope. A getter or setter cannot be extracted from its variable, so the notion of the type of a getter or setter is vacuous; a getter or setter can only be called.

Contrast the following:

var x:Integer = 0;
function f():Integer {return ++x}
function g():Function {return f}
function get h():Function {return f}

f;     // Evaluates to function f
g;     // Evaluates to function g
h;     // Evaluates to function f (not h)
f();   // Evaluates to 1
g();   // Evaluates to function f
h();   // Evaluates to 2
g()(); // Evaluates to 3

See also the discussion of getter and setter syntax.

Unchecked Functions

An unchecked function relaxes argument checking. Unchecked function definitions are provided for compatibility with ECMAScript 3.

A function definition is unchecked if all of the following are true:

  • strict mode is disabled at the point of the function definition;
  • the function is not a class member;
  • the function has no optional or rest parameters;
  • none of the function’s parameters has a declared type;
  • the function does not have a declared return type;
  • the function is not a getter or setter.

An unchecked function also has the prototype attribute set by default.


ECMAScript 4 Netscape Proposal
Core Language
Classes
previousupnext

Monday, April 28, 2003

Class Definitions

Classes are defined using the class keyword.

ClassDefinition  class Identifier Inheritance Block
Inheritance 
   «empty»
|  extends TypeExpressionallowIn

Like other definitions, a class definition may be preceded by one or more attributes, which affect the class’s scope, namespace, and semantics. Every class is also a value and has type Type.

A class definition may only be located at a scope that allows class definitions, defined as follows:

  • The global scope allows class definitions
  • A package scope allows class definitions
  • A class scope allows class definitions
  • If a scope X allows class definitions and a block B is directly inside scope X, then B’s scope also allows class definitions

According to these rules, a class may not be defined inside a function or a compound statement other than a block. If a class B is defined as a member of another class A, then B must be declared static.

Superclasses

A class may have a superclass specified by its extends clause. If omitted, the superclass defaults to Object. The superclass TypeExpression must be a compile-time constant expression without forward references.

A class is a subtype of its superclass.

Body

When a ClassDefinition is evaluated, the following steps take place:

  1. Create a new type t and bind the class’s QualifiedIdentifier to the constant t.
  2. The TypeExpression, if any, in the extends clause is evaluated, and t is made a subtype of its superclass. Any static members of t’s superclass are also defined as properties of the object t.
  3. A new, anonymous namespace for holding the class’s private members is constructed and used for the lexical extent of the Block.
  4. Block is evaluated using a new activation frame initialized with alias bindings for all most derived global members of the superclass. Any static and constructor members defined for Block’s activation frame are added as properties of the object t as they are being defined; these may hide static members inherited from superclasses.
  5. If Block is evaluated successfully (without throwing out an exception), all instance members defined for Block’s top-level scope (along with those inherited from superclasses) are collected to make a template for creating instances of type t.

A ClassDefinition’s Block is evaluated just like any other Block, so it can contain expressions, statements, loops, etc. Such statements that do not contain declarations do not contribute members to the class being declared, but they are evaluated when the class is declared.

Instance Members

A class C’s instance member id becomes a separate property of each instance of C. If c is an instance of C, then such a property can be accessed using the expression c.id. Instance members are inherited from the superclass.

If present, an initializer for a var or const instance member must be a compile-time constant expression.

Methods

A function instance member is called a method. A method may use this to refer to the object on which it was called. The value of this will always be an instance of the class or one of its subclasses. A method may not change the value of this.

A method is not in itself a value and has no type. There is no way to extract an undispatched method from a class. The . operator produces a function (more specifically, a closure) that is already dispatched and has this bound to the left operand of the . operator.

A method is called by combining the . operator with a function call. For example:

class C {
  var x:Integer = 3;
  function m() {return x}
  function n(x) {return x+4}
}

var c = new C;
c.m();                 //
returns 3
c.n(7);                //
returns 11
var f:Function = c.m;  //
f is a zero-argument function with this bound to c
f();                   //
returns 3
c.x = 8;
f();                   //
returns 8

Overriding

A class C may override a method m defined in its superclass s. To do this, C should define a method m' with the same name as m and use the override attribute in the definition of m'. Overriding a method without using the override attribute or using the override attribute when not overriding a method results in an error intended to catch misspelled method names.

The overriding method m' must have the same set of parameters that the overridden method m has.

Let p be any parameter. If m' does not specify a type for p, it inherits the type of p from m. If m' does specify a type for p, it must be the same type as that for p in m. If p is optional in m, then it must also be optional in m' with the same parameter name; however, the default value may differ.

If omitted, the return type of m' defaults to the return type of m. If supplied, the return type of m' must be the same as the return type of m.

A final method cannot be overridden (or further overridden) in the subclasses in which it is visible.

The overridden method m' is put in the same namespaces as method m.

Method m' may call method m using the super operator: either super.m(args) or super this.m(args).

A method may only override another method. An instance variable may only override another instance variable. A getter may override a getter or an instance variable. A setter may override a setter or an instance variable.

Static Members

A class C’s static member id becomes a property of the class object C. This member can be accessed using the expression C.id. static members are inherited from the superclass.

Inherited static variables have only one global value, not one value per subclass. For example, if class C has a static variable v and class D inherits from C, then v can be read or written either as C.v or as D.v; it’s the same variable rather than two separate variables.

Each instance member o named n of class C (other than members that are setters without a corresponding getter) also causes a global member g named n to be defined in C. That global member is currently inaccessible and reserved for a future language extension.

Constructors

A constructor is a function that creates a new instance of a class C. A constructor is defined as a method with the name C without any of the attributes static, virtual, or final. A constructor is invoked using the expression new C or new C(args).

A constructor can refer to its class’s instance variables via this. If a class C inherits from class B, then when B’s constructor is called while creating an instance of C, B’s constructor will be able to call virtual methods of class C on the partially constructed instance. Likewise, B’s constructor could store this into a global variable v and some other function could call a method of C on the partially constructed object v. Class C’s methods can be assured that they are only called on fully initialized instances of C only if neither C nor any of its ancestors contains a constructor that exhibits either of the behaviors above.

A constructor may invoke a return statement as long as that statement does not supply a value; a constructor cannot return a value. The newly created object is returned automatically. A constructor’s return type must be omitted. A constructor always returns a new instance.

A class named C must not define a static member with the name C in any namespace; such usage is reserved for a future extension.

If a class C does not define a constructor or a static function with the name C, a default constructor is automatically defined; that constructor takes the arguments that C’s superclass’s constructor takes, calls that superconstructor with those arguments, and initializes C’s new instance members to their default values.

Calling a Superconstructor

Let C be a class and B its superclass. C’s constructor must call B’s constructor before it accesses this or super or before it returns. The call can be either explicit or implicit; if C’s constructor does not contain any calls to B’s constructor, then a call to B’s constructor with no arguments is automatically inserted as the first statement of C’s constructor. C’s constructor does not have to call B’s constructor when it exits by throwing an exception. C’s constructor may not call B’s constructor again after it already called it.

C’s constructor calls B’s constructor using the statement super(args). This must be a complete statement; it means something different if it is a subexpression of a larger expression. It is not possible to skip class hierarchy levels while constructing an object — if C’s superclass is B and B’s superclass is A, then C’s constructor cannot directly call A’s constructor.


ECMAScript 4 Netscape Proposal
Core Language
Namespaces
previousupnext

Wednesday, September 4, 2002

Namespace Definition

NamespaceDefinition  namespace Identifier

A namespace definition defines a new namespace named Identifier.

A namespace definition may only be located at a scope that allows class definitions. If a namespace is defined as a member of a class, then the namespace must be declared static.

Use Directive

UseDirective  use namespace ParenListExpression

A use namespace directive makes the contents of each namespace in the comma-separated list ParenListExpression accessible without a qualifier.

use namespace directives are lexically scoped and their effect does not extend past the end of the enclosing block, directive group, or substatement group. A use namespace directive may be preceded by attributes; however, all such attributes must evaluate to either true or false.

Name Lookup

The following paragraphs describe what happens when a name is looked up. See also the description of how namespace attributes affect name definitions.

Properties

Conceptually, an instance x is a collection of properties. All properties have property names q::nC, where q is a namespace, n is an identifier, and C is a class. There may be several aliases that refer to the same property (due to either multiple namespace attributes or aliases introduced with the export definition), but a property name q::nC can refer to at most one property of an instance.

An instance x can have several properties q::nC with the same namespace q and name n but different classes C. In the following descriptions, q::n denotes the most derived of these properties, which is the one with the most derived class C.

A property reference can be either unqualified or qualified and is looked up according to the table below. There are two entries in the table for each kind of lookup, depending on whether the left operand of the . operator is a SuperExpression or not. x is an expression that evaluates to an instance, is the set of all scopes enclosing the property reference, and Q is the set of all namespaces q that are used by the scopes in .

Qualified reference x.q::n where q is a namespace Select x’s most derived property q::n. Signal an error if there is no such property.
Qualified reference super x.q::n where q is a namespace This form may only be used for references in the scope of a class C other than Object. Let S be C’s superclass. Among all of x’s properties q::nA select the one whose class A is most derived but still either S or an ancestor of S. Signal an error if there is no such property.
Unqualified reference x.n Let A be the least derived (closest to Object) class such that x contains at least one property named q::nA where q is any element of Q; signal an error if x has no such properties. Let Q' be the set of all namespaces q such that q is in Q and x contains the property named q::nA. Let P be the set of all most derived properties q::n of x such that q is in Q'. If P has only one element p or if all of P’s elements are aliases of one property p, select p; otherwise signal an error.
Unqualified reference super x.n This form may only be used for references in the scope of a class C other than Object. Let S be C’s superclass. Let A be the least derived (closest to Object) class such that x contains at least one property named q::nA where q is any element of Q; signal an error if x has no such properties or if A is not S or an ancestor of S. Let Q' be the set of all namespaces q such that q is in Q and x contains the property named q::nA. For each q in Q' let Bq be the most derived class such that Bq is S or an ancestor of S and x contains the property q::nBq; for each q in Q' let pq be the property q::nBq. Let P be the set of all such properties pq. If P has only one element p or if all of P’s elements are aliases of one property p, select p; otherwise signal an error.
Dynamic reference x[s] Let s evaluate to a string n. Get the property x.public::n. Signal an error if there is no such property.
Dynamic reference super x[s] Let s evaluate to a string n. Get the property super x.public::n. Signal an error if there is no such property.

Note that the only way to access an overridden method is to use super. This is by design to prevent security attacks.

Variables

Conceptually, all variables (which for the purpose of this section also include constants, functions, classes, and such) have qualified names q::n, where q is a namespace and n an identifier. There may be several aliases that refer to the same variable (due to either multiple namespace attributes or aliases introduced with the export definition), but there cannot be two different variables defined using the same qualified name in the same scope.

A variable reference can be either unqualified or qualified and is looked up as follows:

Qualified reference q::n where q is a namespace

Let be the set of all scopes enclosing the qualified reference q::n. Search the scopes in , starting from the innermost one and continuing outwards until a value is found or all scopes have been examined. If no binding has been found after all scopes have been examined, signal an error. For each scope S in , do the following:

  1. If S’s activation frame currently has a binding for q::n, select the value to which q::n is bound.
  2. Otherwise, if S is the scope of the definition of a class C and C or any of its ancestors contains a global member named q::n, then select C’s most derived property q::n. Signal an error if there is no such property.
  3. Otherwise, if S is the top-level scope of a constructor or instance method defined in class C and C or any of its ancestors contains an instance member named q::n, then let this be the value of this corresponding to S’s activation frame and select this’s most derived property q::n. Signal an error if there is no such property.
  4. Otherwise, if S is the scope of the definition of a class C and C or any of its ancestors contains an instance member named q::n, then signal an error.
Unqualified reference n

Let be the set of all scopes enclosing the unqualified reference n. Search the scopes in , starting from the innermost one and continuing outwards until a value is found or all scopes have been examined. If no binding has been found after all scopes have been examined, signal an error. For each scope S in , do the following:

  1. If S’s activation frame A currently has a binding for a variable V, V has a name q::n for some namespace q that is used by a scope in , and that use includes n, then select the value to which q::n is bound in A. If A contains more than one such variable V (not counting aliases of the same variable), signal an error.
  2. Otherwise, if S is the scope of the definition of a class C, C or any of its ancestors contains a global member named q::n for some namespace q that is used by a scope in , then select C’s most derived property q::n. Signal an error if there is no such property or if there are multiple such properties (not counting aliases of the same property) for different q’s.
  3. Otherwise, if S is the top-level scope of a constructor or instance method defined in class C and C or any of its ancestors contains an instance member named q::n for some namespace q that is used by a scope in , then select this’s most derived property q::n using the value of this corresponding to S’s activation frame. Signal an error if there is no such property or if there are multiple such properties (not counting aliases of the same property) for different q’s.
  4. Otherwise, if S is the scope of the definition of a class C, C or any of its ancestors contains an instance member named q::n for some namespace q that is used by a scope in , then signal an error.

 


ECMAScript 4 Netscape Proposal
Core Language
Packages
previousupnext

Wednesday, June 4, 2003

Packages were originally part of the ECMAScript Edition 4 proposal but have been removed due to time constraints. If implemented, packages and import directives might be defined as described below.

Defining Packages

Packages are an abstraction mechanism for grouping and distributing related code. Packages are designed to be linked at run time to allow a program to take advantage of packages written elsewhere or provided by the embedding environment. ECMAScript 4 offers a number of facilities to make packages robust for dynamic linking:

  • Selected package contents can be protected from outside reference.
  • Classes can maintain invariants that cannot be violated by code outside the class and/or package.
  • Function arguments and data structure references can be type-checked to limit the kinds of unexpected inputs the package’s code can experience.
  • Packages can export multiple namespaces, allowing graceful upgrades to packages without changing the code that uses them.

A package is defined using the following syntax:

PackageDefinition  package PackageNameOpt Block
PackageNameOpt 
   «empty»
PackageName 
   String
PackageIdentifiers 

When a package is defined, it may, but is not required to, be given a PackageName, which is either a string or a series of dot-separated identifiers. It is implementation-defined what the restrictions, if any, are on naming packages to avoid clashes with other packages that may be present.

The Block contains the body of a package P. The Block is evaluated at the time package P is loaded. Any public top-level definitions are available to other packages that import package P. Any public class member definitions are available to all other packages, regardless of whether they import package P. Top-level and class definitions defined by P in another namespace N are available to other packages only if they use namespace N or qualify the access with namespace N.

A package is loaded (its body is evaluated) when the package is first imported or invoked directly (if, for example, the package is on an HTML web page). Some standard packages are loaded when the ECMAScript engine first starts up. When a package is loaded, its statements are evaluated in order, which may cause other packages to be loaded along the way when import directives are encountered. Circularities are not allowed in the graph of package imports.

Two attempts to load the same package in the same environment result in sharing of that package. What constitutes an environment is necessarily application-dependent. However, if package P1 loads packages P2 and P3, both of which load package P4, then P4 is loaded only once and thereafter its code and data is shared by P2 and P3.

Javascript does not support package definition circularities (two packages A and B that each import the other), although an implementation may provide such a facility as an extension.

Importing Packages

A package P can reference another package Q via an import directive:

ImportDirective 
   import PackageName
|  import Identifier = PackageName

An import directive may be preceded by attributes; however, all such attributes must evaluate to either true or false.

There are two ways an import directive can name a package to be imported:

  • The PackageName may be PackageIdentifiers. In this case, the system looks for a package with that exact PackageIdentifiers on its implementation-defined search path.
  • The PackageName may be a literal string. In this case, the system interprets the contents of the string in an implementation-defined manner in order to locate the package. Specific ECMAScript 4 embeddings should define the manner in which the contents of the string are interpreted. For example, a browser embedding may be defined to interpret the string as a URI and look for a package at the location given by that URI.

An import directive does the following:

  • Locate the target package specified by PackageName. If the package has not yet been loaded, then load it and wait until the target package’s Block is done evaluating. If loading the target package causes an import of the current package then throw a package circularity exception.
  • Let P be the target package object.
  • If Identifier is given, const-bind it to P in the current scope.
  • For each non-explicit top-level definition N::n (n in namespace N) in P, bind an alias N::n to P’s N::n in the global scope unless N::n is already defined in the global scope.

If package P has a top-level definition n and package Q imports P using import PkgP = P, then package Q can refer to n as either n or PkgP.n. The shorter form n is not available if it conflicts with some other n. If package P has an explicit top-level definition n and package Q imports P, then package Q can refer to that n only as PkgP.n.


ECMAScript 4 Netscape Proposal
Core Language
Pragmas
previousupnext

Tuesday, January 28, 2003

Pragmas allow a script writer to select program modes and options. Pragmas are lexically scoped and their effect does not extend past the end of the enclosing block, directive group, or substatement group.

Pragma  use PragmaItems
PragmaItems 
PragmaItem 
|  PragmaExpr ?
PragmaExpr 
PragmaArgument 
   true
|  false
|  Number
|  - Number
|  - NegatedMinLong
|  String

The keyword use is followed by one or more PragmaItems, each of which consists of an identifier, an optional argument, and an optional ?.

The pragma identifiers below are currently defined. Implementations may define additional identifiers that have meaning as pragmas.

Identifier   Meaning
ecmascript(n) Error if version n of ECMAScript is not supported; otherwise recommends but does not require that the implementation only support ECMAScript version n features.
strict
strict(true)
Strict mode
strict(false) Non-strict mode (default)

The pragma takes effect starting with the directive after the pragma and continues either until the end of the enclosing block or statement group or until overridden by another pragma in the same block or statement group, whichever comes first. If a pragma references the same identifier several times, the last reference takes precedence. The semicolon insertion rule changes implied by the strict pragma apply to the semicolon, if any, ending the use directive that contains that pragma.

If an implementation does not recognize a pragma identifier, then if the PragmaItem ends with a ? then that PragmaItem is ignored; if the PragmaItem does not end with a ? then an error occurs.

Strict Mode

Many parts of ECMAScript 4 are relaxed or unduly convoluted due to compatibility requirements with ECMAScript 3. Strict mode sacrifices some of this compatibility for simplicity and additional error checking. Strict mode is intended to be used in newly written ECMAScript 4 programs, although existing ECMAScript 3 programs may be retrofitted.

The opposite of strict mode is nonstrict mode, which is the default. A program can readily mix strict and nonstrict portions.

Strict mode has the following effects:

  • Line-break semicolon insertion is turned off. (Grammatical semicolon insertion remains turned on.)
  • [no line break] restrictions in grammar productions are ignored. Line breaks can be placed anywhere between input tokens.
  • Variables must be declared.
  • Definition scopes are not hoisted.
  • var and function declarations without attributes or types are initialized at the beginning of a scope.
  • FunctionDefinitions define constants rather than variables.
  • Calls to functions defined under strict mode are checked for the correct number of arguments except in functions that explicitly allow a variable number of arguments. (The mode of the call site does not matter.)
  • Implementations may choose to disable other compatibility extensions such as support for octal literals. These are not officially part of ECMAScript 4 but most implementations support these in nonstrict mode for compatibility with older programs.

An implementation does not have to implement strict mode; however, implementations are encouraged to do so.

See also the rationale.


ECMAScript 4 Netscape Proposal
Libraries
previousupnext

Monday, December 11, 2000


This chapter presents the libraries that accompany the core language.

For the time being, only the libraries new to ECMAScript 4 are described. The basic libraries such as String, Array, etc. carry over from ECMAScript 3.


ECMAScript 4 Netscape Proposal
Libraries
Types
previousupnext

Wednesday, June 4, 2003

Predefined Types

The following types are predefined in ECMAScript 4:

Type Set of Values
Never No values
Void undefined
Null null
Boolean   true and false
Integer Double-precision IEEE floating-point numbers that are mathematical integers, including +0.0, –0.0, +, –, and NaN
Number Double-precision IEEE floating-point numbers, including +0.0, –0.0, +, –, and NaN
char  Single 16-bit unicode characters
String Immutable strings of unicode characters, including null
Function All functions, including null
Array null as well as all arrays
Type All types, including null
Object All values, including null and undefined

Unlike in ECMAScript 3, there is no distinction between objects and primitive values. All values can have methods. Values of some classes are sealed, which disallows addition of dynamic properties. User-defined classes can be made to behave like primitives by using the class modifier final.

The above type names are not reserved words. They can be used as names of local variables or class members. However, they are defined as constants in the global scope, so a package cannot use them to name global variables.

Object is the supertype of all types. Never is the subtype of all types. Never is useful to describe the return type of a function that cannot return normally because it either falls into an infinite loop or always throws an exception. Never cannot be used as the type of a variable or parameter. Void is useful to describe the return type of a function that can return but that does not produce a useful value. See rationale.

A literal number is a member of the type Number; if that literal has an integral value, then it is also a member of type Integer. A literal string is a member of the type String. There are no literals of type char; a char value can be constructed by an explicit or implicit conversion.

An object created with the expression new f where f is a function has the type Object.

User-Defined Types

Any class defined using the class declaration is also a type that denotes the set of all of its and its descendants’ instances. These include the predefined classes, so Object, Date, etc. are all types. null is an instance of a user-defined class. undefined is never an instance of a user-defined class.

Meaning of Types

Types are generally used to restrict the set of objects that can be held in a variable or passed as a function argument. For example, the declaration

var x:Integer;

restricts the values that can be held in variable x to be integers.

A type declaration does not affect the semantics of reading the variable or accessing one of its properties. Thus, as long as expression new MyType() returns a value of type MyType, the following two code snippets are equivalent:

var x:MyType = new MyType();
x.foo();
var x = new MyType();
x.foo();

This equivalence always holds, even if these snippets are inside the declaration of class MyType and foo is a private field of that class. As a corollary, adding true type annotations does not change the meaning of a program.

Type Expressions

The language cannot syntactically distinguish type expressions from value expressions, so a type expression can be any compile-time constant expression that evaluates to a type.

A type is also a value (whose type is Type) and can be used in expressions, assigned to variables, passed to functions, etc. For example, the code

const R:Type = Number;
function abs_val(x:R):R {
  return x<0 ? -x : x;
}

is equivalent to:

function abs_val(x:Number):Number {
  return x<0 ? -x : x;
}

Implicit Coercions

Implicit coercions can take place in the following situations:

  • Assigning a value v to a variable of type t
  • Declaring an uninitialized variable of type t, in which case undefined is implicitly coerced to type t
  • Passing an argument v to a function whose corresponding parameter has type t
  • Returning a result v from a function declared to return a value of type t

In any of these cases, if v t, then v is passed unchanged. If v t, then if t defines an implicit mapping for value v then that mapped v is used; otherwise an error occurs.

Explicit Coercions

An explicit coercion performs more aggressive transformations than an implicit coercion. To invoke an explicit coercion, use the type as a function, passing it the value as an argument:

type(value)

For example, Integer(258.1) returns the integer 258, and String(2+2==4) returns the string "true".

If value is already a member of type, the explicit coercion returns value unchanged. If value can be implicitly coerced to type, the explicit coercion returns the result of the implicit coercion. Otherwise, the explicit coercion uses type’s explicit mapping.


ECMAScript 4 Netscape Proposal
Libraries
Machine Types
previousupnext

Tuesday, March 4, 2003

Purpose

Machine types are low-level numeric types for use in ECMAScript 4 programs. These types provide Java-style integer operations that are useful for communicating between ECMAScript 4 and other programming languages. These types are not intended to replace Number and Integer for general-purpose scripting.

Contents

The following low-level numeric types are available:

Type Suffix  Values
sbyte   Integer values between –128 and 127 inclusive, excluding –0.0
byte   Integer values between 0 and 255 inclusive, excluding –0.0
short   Integer values between –32768 and 32767 inclusive, excluding –0.0
ushort    Integer values between 0 and 65535 inclusive, excluding –0.0
int   Integer values between –2147483648 and 2147483647 inclusive, excluding –0.0
uint   Integer values between 0 and 4294967295 inclusive, excluding –0.0
long L Long integer values between –9223372036854775808 and 9223372036854775807 inclusive
ulong UL Long integer values between 0 and 18446744073709551615 inclusive
float F Single-precision IEEE floating-point numbers, including positive and negative zeroes, infinities, and NaN

The above type names are not reserved words.

8, 16, and 32-bit Integers

The first six types sbyte, byte, short, ushort, int, and uint are all proper subtypes of Integer, which is itself a subtype of Number. A particular number is a member of multiple types. For example, 3.0 is a member of sbyte, byte, short, ushort, int, uint, Integer, Number, and Object, while –2000.0 is a member of short, int, Integer, Number, and Object. ECMAScript does not distinguish between the literals 3 and 3.0 in any way.

All arithmetic operations and comparisons on sbyte, byte, short, ushort, int, and uint values treat them just like they would any other Number values — the operations are performed using full IEEE double-precision arithmetic.

Implicit Coercions

There are no predefined implicit coercions from values of type sbyte, byte, short, ushort, int, or uint other than the coercions predefined on the type Number. The following predefined implicit coercions are applicable when the destination type is sbyte, byte, short, ushort, int, or uint:

  • undefined +0.0
  • –0.0 +0.0
  • long and ulong values within range of the destination type T are converted to equivalent values of type T
  • finite integral float values within range of the destination type T are converted to equivalent values of type T

Note that there are no implicit coercions from +, –, or NaN to sbyte, byte, short, ushort, int, or uint.

Explicit Coercions

There are no predefined explicit coercions from values of type sbyte, byte, short, ushort, int, or uint other than the coercions predefined on the type Number. The predefined explicit coercions below are applicable when the destination type T is sbyte, byte, short, ushort, int, or uint. The notation |T| represents the range of the type T, where |sbyte| = |byte| = 256, |short| = |ushort| = 65536, and |int| = |uint| = 232.

  • undefined +0.0
  • A long or ulong value x is converted to the one value y of type T that satisfies x = y (mod |T|)
  • float values are first converted to equivalent Number values and then converted as below
  • A Number value is first converted to an Integer value x by truncating towards zero if necessary. Then, if x is –0.0, +, –, or NaN, it is converted to +0.0; otherwise, x is converted to the one value y of type T that satisfies x = y (mod |T|)

64-bit Integers

The types long and ulong represent signed and unsigned 64-bit integers. long and ulong literals are written with the suffix L or UL and no exponent or decimal point. Literal values of type long are written as –9223372036854775808L through 9223372036854775807L. Literal values of type ulong are written as 0UL through 18446744073709551615UL.

The types long and ulong are disjoint from Number, so 5L and 5 are different objects, although they compare == and === to each other. 5L and 5UL are also different objects, although they compare == and === to each other.

Negation, addition, subtraction, and multiplication, and modulo (%) on long and ulong values is exact, and long and ulong values may be mixed in an expression. There are five possible cases depending on the mathematical result x:

  • If –9223372036854775808  x  –1, then the result has type long.
  • If 0  x  9223372036854775807, then the result has type ulong if at least one operand has type ulong; otherwise, the result has type long.
  • If 9223372036854775808  x  18446744073709551615, then the result has type ulong.
  • Otherwise, the result is the closest representable Number using the IEEE round-to-nearest mode.

Division involving two long or ulong operands returns the most precise quotient available from among the possible long, ulong, and Number values. In some cases the quotient will be a long or ulong; in other cases the quotient will be a Number. See the semantics for the details.

Division and modulo on long and ulong values can produce the Number values positive or negative infinity or NaN when the divisor is zero.

Addition, subtraction, multiplication, division, and modulo mixing a long or ulong operand with a Number (or any subtype of Number) or float operand first checks whether the Number or float operand is an exact integer (including either +0.0 or –0.0 but not infinities or NaN). If it is, then the computation uses the integral semantics above. If not, then the long or ulong operand is coerced to a Number and the operation is done using Number arithmetic.

The bitwise operations &, |, and ^ are 64 bits wide if at least one operand is a long or ulong, in which case the other operand is truncated to an integer and treated modulo 264 if necessary. The result is a ulong if at least one operand is a ulong; otherwise, the result is a long.

The bitwise shifts <<, >>, and >>> are 64 bits wide if the first operand is a long or ulong. The result is a ulong if the first operand is a ulong; otherwise, the result is a long. >> copies the most significant bit and >>> shifts in zero bits regardless of whether the first operand is a long or ulong.

Comparisons mixing a long or ulong operand with a Number (or any subtype of Number) or float operand compare exact mathematical values without any coercions.

Implicit Coercions

The following predefined implicit coercions are applicable when the destination type is long:

  • undefined 0L
  • ulong values between 0UL and 9223372036854775807UL are converted to equivalent long values
  • Finite Integer values between –9223372036854775808 and 9223372036854775807 are converted to equivalent long values
  • Finite integral float values between –9223372036854775808F and 9223372036854775807F are converted to equivalent long values

The following predefined implicit coercions are applicable when the destination type is ulong:

  • undefined 0UL
  • long values between 0L and 9223372036854775807L are converted to equivalent ulong values
  • Finite Integer values between –0.0 and 18446744073709551615 are converted to equivalent ulong values
  • Finite integral float values between –0.0F and 18446744073709551615F are converted to equivalent ulong values

Note that there are no implicit coercions from NaN or positive or negative infinity to long or ulong.

A long or ulong value can be implicitly coerced to type Number, Integer, or float. The result is the closest representable Number or float value using the same rounding as when a string is converted to a number. If the source is 0L or 0UL then the result is +0.0 or +0.0F.

Explicit Coercions

The predefined explicit coercions below are applicable when the destination type T is long or ulong.

  • undefined 0L or 0UL
  • A long or ulong value x is converted to the one value y of type T that satisfies x = y (mod 264)
  • float values are first converted to equivalent Number values and then converted as below
  • A Number value is first converted to an Integer value x by truncating towards zero if necessary. Then, if x is –0.0, +, –, or NaN, it is converted to 0L or 0UL; otherwise, x is converted to the one value y of type T that satisfies x = y (mod 264).

A long or ulong value x can be explicitly coerced to type Number, Integer, float or String. Explicit coercions to Number, Integer, float are the same as the implicit coercions. Explicit coercions to type String produce the x as a string of decimal digits. Negative values have a minus sign prepended. Zero produces the string "0"; all other values produce strings starting with a non-zero digit.

Single-Precision Floats

The type float represents single-precision IEEE floating-point numbers. float literals are written with the suffix F. float infinities and NaN are separate from Number infinities and NaN.

The type float is disjoint from Number, so 5F and 5 are different objects, although they compare == to each other.

Negating a float value returns a float value. All other arithmetic first converts the float value to the corresponding Number value. The bitwise operations &, |, ^, <<, >>, and >>> coerce any float operands to type Number before proceeding.

Implicit Coercions

The following predefined implicit coercions are applicable when the destination type is float:

  • undefined float(NaN)
  • Number values (including NaN and the infinities) are converted to the closest representable float values using the IEEE round-to-nearest mode
  • long and ulong values are converted to the closest representable float values (excluding –0.0F)

A float value can be implicitly coerced to type Number. The result is the equivalent Number value.


ECMAScript 4 Netscape Proposal
Formal Description
previousupnext

Wednesday, August 14, 2002


This chapter presents the formal syntax and semantics of ECMAScript 4. The syntax notation and semantic notation sections explain the notation used for this description. A simple metalanguage based on a typed lambda calculus is used to specify the semantics.

The syntax and semantic sections are available in both HTML 4.0 and Microsoft Word RTF formats. In the HTML versions each use of a grammar nonterminal or metalanguage value, type, or field is hyperlinked to its definition, making the HTML version preferred for browsing. On the other hand, the RTF version looks much better when printed. The fonts, colors, and other formatting of the various grammar and semantic elements are all encoded as CSS (in HTML) or Word (in RTF) styles and can be altered if desired.

The syntax and semantics sections are machine-generated from code supplied to a small engine that can type-check and execute the semantics directly. This engine is in the CVS tree at mozilla/js2/semantics; the input files are at mozilla/js2/semantics/JS20.


ECMAScript 4 Netscape Proposal
Formal Description
Semantic Notation
previousupnext

Friday, June 13, 2003

The semantics of ECMAScript 4 are written in Algol-like pseudocode. The following sections define the notation used to write the semantics’ concepts, expressions, procedures, and actions.

Operators

The table below summarizes operators used in expressions in this document. The operators are listed in groups in order from the highest precedence (tightest-binding) to the lowest precedence (loosest-binding). Other than the relational operators, operators in the same group have the same precedence and associate left-to-right, so, for example, 7–3+2–1 means ((7–3)+2)–1 instead of 7–(3+(2–1)) or (7–(3+2))–1. As is traditional in mathematics, the relational operators cascade, so

a = b  c < d

means

a = b and b  c and c < d

Parentheses are used to override precedences or clarify expressions.

Expressions used in describing algorithms may perform side effects. Except for and, or, and ?:, the operators compute all of their operands left-to-right, and if computation of any operand throws an exception e, then the operator immediately propagates e without computing any following operands.

Group Operator Description
Nonassociative (x) Return x. Parentheses are used to override operator precedence.
{x1x2, ... , xn} Set or semantic domain with the elements x1x2, ... , xn
{f(x) | x  A}
{f(x) | x  A such that predicate(x)}
Set comprehension
[x0x1, ... , xn–1] List with the elements x0x1, ... , xn–1
[f(x| x  u]
[f(x| x  u such that predicate(x)]
List comprehension
Namelabel1x1, ... , labelnxn
Name
Tuple constructor
|x| Absolute value of a number x, cardinality of a set x, or length of a list x
x Floor of x
x Ceiling of x
Action[nonterminali] This notation is used inside an action for a grammar production that has nonterminal nonterminal on the production’s left or right side. Return the value of action Action invoked on the ith instance of nonterminal nonterminal on the left or right side of . The subscript i can be omitted if it is 1 and there is only one instance of nonterminal nonterminal on ’s right side.
nonterminali This notation is used inside an action for a grammar production that has nonterminal nonterminal on the production’s left or right side. Furthermore, every complete expansion of grammar nonterminal nonterminal expands into a single character.
Return the character to which the ith instance of nonterminal nonterminal on the right side of expands. The subscript i can be omitted if there is only one instance of nonterminal nonterminal in . If the subscript is omitted and nonterminal nonterminal appears on the left side of , then this expression returns the single character to which this whole production expands.
Suffix ilong Convert integer i to a Long
iulong Convert integer i to a ULong
xf32 Convert real number x to the “closest” Float32 value by calling realToFloat32(x)
xf64 Convert real number x to the “closest” Float64 value by calling realToFloat64(x)
xy x raised to the yth power
u[i] ith element of list u
u[i ... j]
u[i ...]
Slice of list u
u[i \ x]   List element substitution
a.label Field named label of tuple or record a
T{} Semantic domain of all sets whose elements are members of semantic domain T
T[] Semantic domain of all lists whose elements are members of semantic domain T
f(x1, ..., xn) Procedure call
Prefix new Namelabel1x1, ... , labelnxn Record constructor
x Real number negation
min A Smallest element of a set
max A Largest element of a set
Factor x  y Real number product
x / y Real number quotient (y must not be zero)
x mod y Real number remainder (y must not be zero)
A  B Set intersection
T1  T2  ...  Tn  T
()  T
T1  T2  ...  Tn  ()
()  ()
Semantic domain of procedures
Term x + y Real number addition
x – y Real number subtraction or set difference
u  v List concatenation
A  B Set union
Relational x = y
x  y
Equality and inequality predicates on tags, real numbers, sets, booleans, characters, lists, strings, tuples, and records. Values of differing kinds (such as the boolean true and the character ‘A’) are always considered unequal.
x < y
x  y
x > y
x  y
Order predicates on real numbers, characters, and strings.
x  A
x  A
Set membership predicates
A  B Subset predicate
A  B Proper subset predicate
Negation not a Logical negation
Conjunction a and b Short-circuiting logical conjunction
Disjunction a or b Short-circuiting logical disjunction
a xor b Logical exclusive or
Conditional a ? x : y Conditional
some x  A satisfies predicate(x)
every x  A satisfies predicate(x)
Set or list quantifiers

Semantic Domains

Semantic domains describe the possible values that a variable might take on in an algorithm. The algorithms are constructed in a way that ensures that these constraints are always met, regardless of any valid or invalid programmer or user input or actions.

A semantic domain can be intuitively thought of as a set of possible values, and, in fact, any set of values explicitly described in this document is also a semantic domain. Nevertheless, semantic domains have a more precise mathematical definition in domain theory (see for example [Schmidt86]) that allows one to define semantic domains recursively without encountering paradoxes such as trying to define a set A whose members include all functions mapping values from A to Integer. The problem with an ordinary definition of such a set A is that the cardinality of the set of all functions mapping A to Integer is always strictly greater than the cardinality of A, leading to a contradiction. Domain theory uses a least fixed point construction to allow A to be defined as a semantic domain without encountering problems.

Semantic domains have names in Capitalized Red Small Caps. Such a name is to be considered distinct from a tag or regular variable with the same name, so Undefined, undefined, and undefined are three different and independent entities.

A variable v is constrained using the notation

vT

where T is a semantic domain. This constraint indicates that the value of v will always be a member of the semantic domain T. These declarations are informative (they may be dropped without affecting the semantics’ correctness) but useful in understanding the semantics. For example, when the semantics state that xInteger then one does not have to worry about what happens when x has the value true or +f64.

The constraints can be proven statically. The ECMAScript semantics have been machine-checked to ensure that every constraint holds.

Tags

Tags are computational tokens with no internal structure. Tags are written using a dark red font. Two tags are equal if and only if they have the same name.

Each tag that does not also name a tuple or a record is defined using the notation:

tag name;

In the HTML version of the semantics, each use of a tag’s name is linked back to its definition.

Booleans

The tags true and false represent booleans. Boolean is the two-element semantic domain {truefalse}.

Let a and b be booleans and x and y any values. In addition to = and , the following operations can be done on them:

Notation   Description
not a true if a is false; false if a is true
a and b If a is false, return false without computing b; if a is true, return the value of b
a or b If a is false, return the value of b; if a is true, return true without computing b
a xor b true if a is true and b is false or a is false and b is true; false otherwise. a xor b is equivalent to a  b
a ? x : y If a is true, compute and return the value of x; if a is false, compute and return the value of y

Note that the and, or, and ?: operators short-circuit. These are the only operators that do not always compute all of their operands.

Sets

A set is an unordered, possibly infinite collection of elements. Each element may occur at most once in a set. There must be an equivalence relation = defined on all pairs of the set’s elements. Elements of a set may themselves be sets.

A set is denoted by enclosing a comma-separated list of values inside braces:

{element1element2, ... , elementn}

The empty set is written as {}. Any duplicate elements are included only once in the set.

For example, the set {3, 0, 10, 11, 12, 13, –5} contains seven integers.

Sets of either integers or characters can be abbreviated using the ... range operator, which generates inclusive ranges of integers or character code points. For example, the above set can also be written as {0, –5, 3 ... 3, 10 ... 13}.

If the beginning of the range is equal to the end of the range, then the range consists of only one element: {7 ... 7} is the same as {7}. If the end of the range is one less than the beginning, then the range contains no elements: {7 ... 6} is the same as {}. The end of the range is never more than one less than the beginning.

A set can also be written using the set comprehension notation

{f(x) | x  A}

which denotes the set of the results of computing expression f on all elements x of set A. A predicate can be added:

{f(x) | x  A such that predicate(x)}

denotes the set of the results of computing expression f on all elements x of set A that satisfy the predicate expression. There can also be more than one free variable x and set A, in which case all combinations of free variables’ values are considered. For example,

{x | x  Integer such that x2 < 10} = {–3, –2, –1, 0, 1, 2, 3};
{x2 | x  {–5, –1, 1, 2, 4}} = {1, 4, 16, 25};
{x10 + y | x  {1, 2, 4}, y  {3, 5}} = {13, 15, 23, 25, 43, 45}.

The same notation is used for operations on sets and on semantic domains. Let A and B be sets (or semantic domains) and x and y be values. The following operations can be done on them:

Notation   Description
x  A true if x is an element of A and false if not
x  A false if x is an element of A and true if not
|A| The number of elements in A (only used on finite sets)
min A The value m that satisfies both m  A and for all elements x  A, x  m (only used on nonempty, finite sets whose elements have a well-defined order relation)
max A The value m that satisfies both m  A and for all elements x  A, x  m (only used on nonempty, finite sets whose elements have a well-defined order relation)
A  B The intersection of A and B (the set or semantic domain of all values that are present both in A and in B)
A  B The union of A and B (the set or semantic domain of all values that are present in at least one of A or B)
A – B The difference of A and B (the set or semantic domain of all values that are present in A but not B)
A = B true if A and B are equal and false otherwise. A and B are equal if every element of A is also in B and every element of B is also in A.
A  B false if A and B are equal and true otherwise
A  B true if A is a subset of B and false otherwise. A is a subset of B if every element of A is also in B. Every set is a subset of itself. The empty set {} is a subset of every set.
A  B true if A is a proper subset of B and false otherwise. A  B is equivalent to A  B and A  B.

If T is a semantic domain, then T{} is the semantic domain of all sets whose elements are members of T. For example, if T = {1,2,3}, then T{} = {{}, {1}, {2}, {3}, {1,2}, {1,3}, {2,3}, {1,2,3}}. The empty set {} is a member of T{} for any semantic domain T.

In addition to the above, the some and every quantifiers can be used on sets (see also lists). The quantifier

some x  A satisfies predicate(x)

returns true if there exists at least one element x in set A such that predicate(x) computes to true. If there is no such element x, then the some quantifier’s result is false. If the some quantifier returns true, then variable x is left bound to any element of A for which predicate(x) computes to true; if there is more than one such element x, then one of them is chosen arbitrarily. For example,

some x  {3, 16, 19, 26} satisfies x mod 10 = 6

evaluates to true and leaves x set to either 16 or 26. Other examples include:

(some x  {3, 16, 19, 26} satisfies x mod 10 = 7) = false;
(some x  {} satisfies x mod 10 = 7) = false;
(some x  {“Hello”} satisfies true) = true and leaves x set to the string “Hello”;
(some x  {} satisfies true) = false.

The quantifier

every x  A satisfies predicate(x)

returns true if there exists no element x in set A such that predicate(x) computes to false. If there is at least one such element x, then the every quantifier’s result is false. As a degenerate case, the every quantifier is always true if the set A is empty. For example,

(every x  {3, 16, 19, 26} satisfies x mod 10 = 6) = false;
(every x  {6, 26, 96, 106} satisfies x mod 10 = 6) = true;
(every x  {} satisfies x mod 10 = 6) = true.

Real Numbers

Numbers written in plain font are exact mathematical real numbers. Numbers can be written with or without a decimal point. Integers preceded with “0x” are hexadecimal (base 16). 4294967296, 4294967296.000, 0x100000000, and 232 are all the same number. 0.1 is the exact value 1/10.

Integer is the semantic domain of all integers {... –3, –2, –1, 0, 1, 2, 3 ...}. 3.0, 3, 0xFF, and –10100 are all integers.

Rational is the semantic domain of all rational numbers. Every integer is also a rational number: Integer  Rational. 3, 1/3, 7.5, –12/7, and 2–5 are examples of rational numbers.

Real is the semantic domain of all real numbers. Every rational number is also a real number: Rational  Real. is an example of a real number slightly larger than 3.14.

Let x and y be real numbers. The following operations can be done on them and always produce exact results:

Notation   Description
x Negation
|x| Absolute value
x + y Sum
x – y Difference
x  y Product
x / y Quotient (y must not be zero)
xy x raised to the yth power (used only when either x0 and y is an integer or x is any number and y>0)
x Floor of x, which is the unique integer i such that i  x < i+1.  = 3, –3.5 = –4, and 7 = 7.
x Ceiling of x, which is the unique integer i such that i–1 < x  i.  = 4, –3.5 = –3, and 7 = 7.
x mod y x modulo y, which is defined as x – yx/y. y must not be zero. 10 mod 7 = 3, and –1 mod 7 = 6.
log10(x) The exact base-10 logarithm of x (x will always be greater than zero)

Real numbers can be compared using =, , <, , >, and . The result is either true or false.

Bitwise Integer Operators

The four procedures below perform bitwise operations on integers. The integers are treated as though they were written in infinite-precision two’s complement binary notation, with each 1 bit representing true and 0 bit representing false.

More precisely, any integer x can be represented as an infinite sequence of bits ai where the index i ranges over the nonnegative integers and every ai  {0, 1}. The sequence is traditionally written in reverse order:

..., a4, a3, a2, a1, a0

The unique sequence corresponding to an integer x is generated by the formula

ai = x / 2i mod 2

If x is zero or positive, then its sequence will have infinitely many consecutive leading 0’s, while a negative integer x will generate a sequence with infinitely many consecutive leading 1’s. For example, 6 generates the sequence ...0...0000110, while –6 generates ...1...1111010.

The logical and, or, and xor operations below operate on corresponding elements of the sequences ai and bi generated by the two parameters x and y. The result is another infinite sequence of bits ci. The result of the operation is the unique integer z that generates the sequence ci. For example, anding corresponding elements of the sequences generated by 6 and –6 yields the sequence ...0...0000010, which is the sequence generated by the integer 2. Thus, bitwiseAnd(6, –6) = 2.

Procedure   Description
bitwiseAnd(xIntegeryInteger): Integer The bitwise and of x and y
bitwiseOr(xIntegeryInteger): Integer The bitwise or of x and y
bitwiseXor(xIntegeryInteger): Integer The bitwise xor of x and y
bitwiseShift(xIntegercountInteger): Integer Shift x to the left by count bits. If count is negative, shift x to the right by –count bits. Bits shifted out of the right end are lost; bit shifted in at the right end are zero. bitwiseShift(xcount) is exactly equivalent to x  2count.

Characters

Characters enclosed in single quotes ‘ and ’ represent Unicode characters with code points ranging from 0000 to 10FFFF hexadecimal. Even though Unicode does not define characters for some of these code points, in this specification any of these 1114112 code points is considered to be a valid character. Examples of characters include ‘A’, ‘b’, ‘«LF»’, ‘«uFFFF»’, ‘«U00010000»’ and , ‘«U0010FFFF»’ (see also the notation for non-ASCII characters).

Unicode has the notion of code points, which are numerical indices of characters in the Unicode character table, as well as code units, which are numerical values for storing characters in a particular representation. ECMAScript is designed to make it appear that strings are represented in the UTF-16 representation, which means that a code unit is a 16-bit value (an implementation may store strings in other formats such as UTF-8, but it must make it appear for indexing and character extraction purposes as if strings were sequences of 16-bit code units). For convenience this specification does not distinguish between code units and code points in the range from 0000 to FFFF hexadecimal.

Char16 is the semantic domain of the 65536 Unicode characters in the set {‘«u0000»’ ... ‘«uFFFF»’}. These characters form Unicode’s Basic Multilingual Plane. These characters have code points between 0000 and FFFF hexadecimal. Code units are also represented by values in the Char16 semantic domain.

SupplementaryChar is the semantic domain of the 1048576 Unicode characters in the set {‘«U00010000»’ ... ‘«U0010FFFF»’}. These are Unicode’s supplementary characters with code points between 10000 and 10FFFF hexadecimal. Since these characters are not members of the Char16 domain, they cannot be stored directly in strings of Char16 code units. Instead, whereever necessary the semantic algorithms convert supplementary characters into pairs of surrogate code units before storing them into strings. The first surrogate code unit h is in the set {‘«uD800»’ ... ‘«uDBFF»’} and the second surrogate code unit l is in the set {‘«uDC00»’ ... ‘«uDFFF»’}; together they encode the supplementary character with the code point value 0x10000 + (char16ToInteger(h) – 0xD800)0x400 + char16ToInteger(l) – 0xDC00.

Char21 is the semantic domain of all 1114112 Unicode characters {‘«u0000»’ ... ‘«U0010FFFF»’}.

Char21 = Char16  SupplementaryChar

Characters can be compared using =, , <, , >, and . These operators compare code point values, so ‘A’ = ‘A’, ‘A’ < ‘B’, ‘A’ < ‘a’, and ‘«uFFFF»’ < ‘«U00010000»’ are all true.

Character Conversions

The following procedures convert between characters and integers:

Procedure   Description
char16ToInteger(cChar16): {0 ... 0xFFFF} The number of character c’s Unicode code point or code unit
char21ToInteger(cChar21): {0 ... 0x10FFFF} The number of character c’s Unicode code point
integerToChar16(i: {0 ... 0xFFFF}): Char16 The character whose Unicode code point or code unit number is i
integerToSupplementaryChar(i: {0x10000 ... 0x10FFFF}): SupplementaryChar The character whose Unicode code point number is i
integerToChar21(i: {0 ... 0x10FFFF}): Char21 The character whose Unicode code point number is i

The procedure digitValue is defined as follows:

proc digitValue(c: {‘0’ ... ‘9’, ‘A’ ... ‘Z’, ‘a’ ... ‘z’}): {0 ... 35}
case c of
{‘0’ ... ‘9’} do return char16ToInteger(c) – char16ToInteger(‘0’);
{‘A’ ... ‘Z’} do return char16ToInteger(c) – char16ToInteger(‘A’) + 10;
{‘a’ ... ‘z’} do return char16ToInteger(c) – char16ToInteger(‘a’) + 10
end case
end proc;

Lists

A finite ordered list of zero or more elements is written by listing the elements inside bold brackets:

[element0element1, ... , elementn–1]

For example, the following list contains four strings:

[parsley”, “sage”, “rosemary”, “thyme]

The empty list is written as [].

Unlike a set, the elements of a list are indexed by integers starting from 0. A list can contain duplicate elements.

A list can also be written using the list comprehension notation

[f(x| x  u]

which denotes the list [f(u[0]), f(u[1]), ... , f(u[|u|–1])] whose elements consist of the results of applying expression f to each corresponding element of list u. x is the name of the parameter in expression f. A predicate can be added:

[f(x| x  u such that predicate(x)]

denotes the list of the results of computing expression f on all elements x of list u that satisfy the predicate expression. The results are listed in the same order as the elements x of list u. For example,

[x2 | x  [–1, 1, 2, 3, 4, 2, 5]] = [1, 1, 4, 9, 16, 4, 25]
[x+1 | x  [–1, 1, 2, 3, 4, 5, 3, 10] such that x mod 2 = 1] = [0, 2, 4, 6, 4]

Let u = [e0e1, ... , en–1] and v = [f0f1, ... , fm–1] be lists, e be an element, i and j be integers, and x be a value. The operations below can be done on lists. The operations are meaningful only when their preconditions are met; the semantics never use the operations below without meeting their preconditions.

Notation   Precondition Description
|u| The length n of the list
u[i]  i < |u| The ith element ei.
u[i ... j]  i  j+1  |u| The list slice [eiei+1, ... , ej] consisting of all elements of u between the ith and the jth, inclusive. The result is the empty list [] if j=i–1.
u[i ...]  i  |u| The list slice [eiei+1, ... , en–1] consisting of all elements of u between the ith and the end. The result is the empty list [] if i=n.
u[i \ x]    i < |u| The list [e0, ... , ei–1xei+1, ... , en–1] with the ith element replaced by the value x and the other elements unchanged
u  v The concatenated list [e0e1, ... , en–1f0f1, ... , fm–1]
repeat(ei)  0 The list [ee, ... , e] of length i containing i identical elements e
u = v true if the lists u and v are equal and false otherwise. Lists u and v are equal if they have the same length and all of their corresponding elements are equal.
u  v false if the lists u and v are equal and true otherwise.

Lists are functional — there is no notation for modifying a list in place.

If T is a semantic domain, then T[] is the semantic domain of all lists whose elements are members of T. The empty list [] is a member of T[] for any semantic domain T.

In addition to the above, the some and every quantifiers can be used on lists just as on sets:

some x  u satisfies predicate(x)
every x  u satisfies predicate(x)

These quantifiers’ behavior on lists is analogous to that on sets, except that, if the some quantifier returns true then it leaves variable x set to the first element of list u that satisfies condition predicate(x). For example,

some x  [3, 36, 19, 26] satisfies x mod 10 = 6

evaluates to true and leaves x set to 36.

Strings

A list of Char16 code units is called a string. In addition to the normal list notation, for notational convenience a string can also be written as zero or more characters enclosed in double quotes (see also the notation for non-ASCII characters). Thus,

Wonder«LF»

is equivalent to:

[W’, ‘o’, ‘n’, ‘d’, ‘e’, ‘r’, ‘«LF»]

The empty string is written as “”.

A string holds code units, not code points. Supplementary Unicode characters are represented as pairs of surrogate code units when stored in strings.

In addition to all of the other list operations, <, , >, and are defined on strings. A string x is less than string y when y is not the empty string and either x is the empty string, the first code unit of x is less than the first code unit of y, or the first code unit of x is equal to the first code unit of y and the rest of string x is less than the rest of string y.

Note that these relations compare code units, not code points, which can produce unexpected effects if a string contains supplementary characters expanded into a pairs of surrogates. For example, even though ‘«uFFFF»’ < ‘«U00010000»’, the supplementary character ‘«U00010000»’ is represented in a string as “«uD800»«uDC00»”, and, by the above rules, “«uFFFF»” > “«uD800»«uDC00»”.

String is the semantic domain of all strings. String = Char16[].

Tuples

A tuple is an immutable aggregate of values comprised of a name and zero or more labeled fields.

The pseudocode defines each tuple and describes its fields. A tuple definition has the form

tuple Name
label1T1,
... ,
labelnTn
end tuple;

and defines tuples with name Name to have n fields with semantic domains T1 through Tn respectively. In the HTML version of the semantics, each use of a tuple’s Name is linked back to its definition.

After Name is defined, the notation

Namelabel1v1, ... , labelnvn

represents a tuple with name Name and values v1 through vn for fields labeled label1 through labeln respectively. Each value vi is a member of the corresponding semantic domain Ti. When most of the fields are copied from an existing tuple a, this notation can be abbreviated as

Namelabeli1vi1, ... , labelikvik, other fields from a

which represents a tuple with name Name and values vi1 through vik for fields labeled labeli1 through labelik respectively and the values of correspondingly labeled fields from a for all other fields.

If a is the tuple Namelabel1v1, ... , labelnvn, then

a.labeli

returns the ith field’s value vi. Tuples are functional — there is no notation for modifying a tuple in place. In the HTML version of the semantics, each use of labeli is linked back to a’s type.

The equality operators = and may be used to compare tuples. Tuples are equal when they have the same name and their corresponding fields’ values are equal.

The notation

Name

represents the semantic domain of all tuples with name Name.

Records

A record is a mutable aggregate of values similar to a tuple but with different equality behavior.

A record is comprised of a name and an address. The address points to a mutable data structure comprised of zero or more labeled fields. The address acts as the record’s serial number — every record allocated by new (see below) gets a different address, including records created by identical expressions or even the same expression used twice.

The pseudocode defines each record and describes its fields. A record definition has the form

record Name
label1T1,
... ,
labelnTn
end record;

and defines records with name Name to have n fields with semantic domains T1 through Tn respectively. In the HTML version of the semantics, each use of a record’s Name is linked back to its definition.

After Name is defined, the expression

new Namelabel1v1, ... , labelnvn

creates a record with name Name and a new address . The fields labeled label1 through labeln at address are initialized with values v1 through vn respectively. Each value vi is a member of the corresponding semantic domain Ti. A labelkvk pair may be omitted from a new expression, which indicates that the initial value of field labelk does not matter because the semantics will always explicitly write a value into that field before reading it.

When most of the fields are copied from an existing record a, the new expression can be abbreviated as

new Namelabeli1vi1, ... , labelikvik, other fields from a

which represents a record b with name Name and a new address . The fields labeled labeli1 through labelik at address are initialized with values vi1 through vik respectively; the other fields at address are initialized with the values of correspondingly labeled fields from a’s address.

If a is a record with name Name and address , then

a.labeli

returns the current value v of the ith field at address . That field may be set to a new value w, which must be a member of the semantic domain Ti, using the assignment

a.labeli  w

after which a.labeli will evaluate to w. Any record with a different address is unaffected by the assignment. In the HTML version of the semantics, each use of labeli is linked back to a’s type.

The equality operators = and may be used to compare records. Records are equal if and only if they have the same address.

The notation

Name

represents the infinite semantic domain of all records that have name Name and all addresses.

ECMAScript Numeric Types

ECMAScript does not support exact real numbers as one of the programmer-visible data types. Instead, ECMAScript numbers have finite range and precision. The semantic domain of all programmer-visible numbers representable in ECMAScript is GeneralNumber, defined as the union of four basic numeric semantic domains Long, ULong, Float32, and Float64:

GeneralNumber = Long  ULong  Float32  Float64

The four basic numeric semantic domains are all disjoint from each other and from the semantic domains Integer, Rational, and Real.

The semantic domain FiniteGeneralNumber is the subtype of all finite values in GeneralNumber:

FiniteGeneralNumber = Long  ULong  FiniteFloat32  FiniteFloat64

Signed Long Integers

Programmer-visible signed 64-bit long integers are represented by the semantic domain Long. These are wrapped in a tuple to keep them disjoint from members of the semantic domains ULong, Float32, and Float64.

tuple Long
value: {–263 ... 263 – 1}
end tuple

Shorthand Notation

In this specification, when i is an integer between –263 and 263 – 1, the notation ilong indicates the result of Longvaluei, which is the integer i wrapped in a Long tuple.

Unsigned Long Integers

Programmer-visible unsigned 64-bit long integers are represented by the semantic domain ULong. These are wrapped in a tuple to keep them disjoint from members of the semantic domains Long, Float32, and Float64.

tuple ULong
value: {0 ... 264 – 1}
end tuple

Shorthand Notation

In this specification, when i is an integer between 0 and 264 – 1, the notation iulong indicates the result of ULongvaluei, which is the integer i wrapped in a ULong tuple.

Single-Precision Floating-Point Numbers

Float32 is the semantic domain of all representable single-precision floating-point IEEE 754 values, with all not-a-number values considered indistinguishable from each other. Float32 is the union of the following semantic domains:

Float32 = FiniteFloat32  {+f32f32NaNf32};
FiniteFloat32 = NonzeroFiniteFloat32  {+zerof32–zerof32}

The non-zero finite values are wrapped in a tuple to keep them disjoint from members of the semantic domains Long, ULong, and Float64. The remaining values are the tags +zerof32 (positive zero), –zerof32 (negative zero), +f32 (positive infinity), f32 (negative infinity), and NaNf32 (not a number).

tuple NonzeroFiniteFloat32
valueNormalizedFloat32Values  DenormalizedFloat32Values
end tuple

There are 4261412864 (that is, 232–225) normalized values:

NormalizedFloat32Values = {sm2e | s  {–1, 1}, m  {223 ... 224–1}, e  {–149 ... 104}}

m is called the significand.

There are also 16777214 (that is, 224–2) denormalized non-zero values:

DenormalizedFloat32Values = {sm2–149 | s  {–1, 1}, m  {1 ... 223–1}}

m is called the significand.

Members of the semantic domain NonzeroFiniteFloat32 with value greater than zero are called positive finite. The remaining members of NonzeroFiniteFloat32 are called negative finite.

Since floating-point numbers are either tags or tuples wrapping rational numbers, the notation = and may be used to compare them. Note that = is false for different tags, so +zerof32  –zerof32 but NaNf32 = NaNf32. The ECMAScript x == y and x === y operators have different behavior for Float32 values, defined by isEqual and isStrictlyEqual.

Shorthand Notation

In this specification, when x is a real number, the notation xf32 indicates the result of realToFloat32(x), which is the “closest” Float32 value as defined below. Thus, 3.4 is a Real number, while 3.4f32 is a Float32 value (whose exact value is actually 3.400000095367431640625). The positive finite Float32 values range from 10–45f32 to (3.4028235  1038)f32.

Conversion

The procedure realToFloat32 converts a real number x into the applicable element of Float32 as follows:

proc realToFloat32(xReal): Float32
sRational{}  NormalizedFloat32Values  DenormalizedFloat32Values  {–2128, 0, 2128};
Let aRational be the element of s closest to x (i.e. such that |ax| is as small as possible). If two elements of s are equally close, let a be the one with an even significand; for this purpose –2128, 0, and 2128 are considered to have even significands.
if a = 2128 then return +f32
elsif a = –2128 then return f32
elsif a  0 then return NonzeroFiniteFloat32valuea
elsif x < 0 then return –zerof32
else return +zerof32
end if
end proc

This procedure corresponds exactly to the behavior of the IEEE 754 “round to nearest” mode.

The procedure truncateFiniteFloat32 truncates a FiniteFloat32 value to an integer, rounding towards zero:

proc truncateFiniteFloat32(xFiniteFloat32): Integer
if x  {+zerof32–zerof32then return 0 end if;
rRational  x.value;
if r > 0 then return r else return r end if
end proc

Arithmetic

The following table defines negation of Float32 values using IEEE 754 rules. Note that exprf32 indicates the result of realToFloat32(expr).

float32Negate(xFloat32): Float32
x Result
f32 +f32
negative finite (–x.value)f32
–zerof32 +zerof32
+zerof32 –zerof32
positive finite (–x.value)f32
+f32 f32
NaNf32 NaNf32

Double-Precision Floating-Point Numbers

Float64 is the semantic domain of all representable double-precision floating-point IEEE 754 values, with all not-a-number values considered indistinguishable from each other. Float64 is the union of the following semantic domains:

Float64 = FiniteFloat64  {+f64f64NaNf64};
FiniteFloat64 = NonzeroFiniteFloat64  {+zerof64–zerof64}

The non-zero finite values are wrapped in a tuple to keep them disjoint from members of the semantic domains Long, ULong, and Float32. The remaining values are the tags +zerof64 (positive zero), –zerof64 (negative zero), +f64 (positive infinity), f64 (negative infinity), and NaNf64 (not a number).

tuple NonzeroFiniteFloat64
valueNormalizedFloat64Values  DenormalizedFloat64Values
end tuple

There are 18428729675200069632 (that is, 264–254) normalized values:

NormalizedFloat64Values = {sm2e | s  {–1, 1}, m  {252 ... 253–1}, e  {–1074 ... 971}}

m is called the significand.

There are also 9007199254740990 (that is, 253–2) denormalized non-zero values:

DenormalizedFloat64Values = {sm2–1074 | s  {–1, 1}, m  {1 ... 252–1}}

m is called the significand.

Members of the semantic domain NonzeroFiniteFloat64 with value greater than zero are called positive finite. The remaining members of NonzeroFiniteFloat64 are called negative finite.

Since floating-point numbers are either tags or tuples wrapping rational numbers, the notation = and may be used to compare them. Note that = is false for different tags, so +zerof64  –zerof64 but NaNf64 = NaNf64. The ECMAScript x == y and x === y operators have different behavior for Float64 values, defined by isEqual and isStrictlyEqual.

Shorthand Notation

In this specification, when x is a real number, the notation xf64 indicates the result of realToFloat64(x), which is the “closest” Float64 value as defined below. Thus, 3.4 is a Real number, while 3.4f64 is a Float64 value (whose exact value is actually 3.399999999999999911182158029987476766109466552734375). The positive finite Float64 values range from (5  10–324)f64 to (1.7976931348623157  10308)f64.

Conversion

The procedure realToFloat64 converts a real number x into the applicable element of Float64 as follows:

proc realToFloat64(xReal): Float64
sRational{}  NormalizedFloat64Values  DenormalizedFloat64Values  {–21024, 0, 21024};
Let aRational be the element of s closest to x (i.e. such that |ax| is as small as possible). If two elements of s are equally close, let a be the one with an even significand; for this purpose –21024, 0, and 21024 are considered to have even significands.
if a = 21024 then return +f64
elsif a = –21024 then return f64
elsif a  0 then return NonzeroFiniteFloat64valuea
elsif x < 0 then return –zerof64
else return +zerof64
end if
end proc

This procedure corresponds exactly to the behavior of the IEEE 754 “round to nearest” mode.

The procedure float32ToFloat64 converts a Float32 number x into the corresponding Float64 number as defined by the following table:

proc float32ToFloat64(xFloat32): Float64
x Result
f32 f64
–zerof32 –zerof64
+zerof32 +zerof64
+f32 +f64
NaNf32 NaNf64
Any NonzeroFiniteFloat32 value NonzeroFiniteFloat64valuex.value

The procedure truncateFiniteFloat64 truncates a FiniteFloat64 value to an integer, rounding towards zero:

proc truncateFiniteFloat64(xFiniteFloat64): Integer
if x  {+zerof64–zerof64then return 0 end if;
rRational  x.value;
if r > 0 then return r else return r end if
end proc

Arithmetic

The following tables define procedures that perform common arithmetic on Float64 values using IEEE 754 rules. Note that exprf64 indicates the result of realToFloat64(expr).

float64Abs(xFloat64): Float64
x Result
f64 +f64
negative finite (–x.value)f64
–zerof64 +zerof64
+zerof64 +zerof64
positive finite x
+f64 +f64
NaNf64 NaNf64
float64Negate(xFloat64): Float64
x Result
f64 +f64
negative finite (–x.value)f64
–zerof64 +zerof64
+zerof64 –zerof64
positive finite (–x.value)f64
+f64 f64
NaNf64 NaNf64
float64Add(xFloat64yFloat64): Float64
x y
f64 negative finite –zerof64 +zerof64 positive finite +f64 NaNf64
f64 f64 f64 f64 f64 f64 NaNf64 NaNf64
negative finite f64 (x.value + y.value)f64 x x (x.value + y.value)f64 +f64 NaNf64
–zerof64 f64 y –zerof64 +zerof64 y +f64 NaNf64
+zerof64 f64 y +zerof64 +zerof64 y +f64 NaNf64
positive finite f64 (x.value + y.value)f64 x x (x.value + y.value)f64 +f64 NaNf64
+f64 NaNf64 +f64 +f64 +f64 +f64 +f64 NaNf64
NaNf64 NaNf64 NaNf64 NaNf64 NaNf64 NaNf64 NaNf64 NaNf64
float64Subtract(xFloat64yFloat64): Float64
x y
f64 negative finite –zerof64 +zerof64 positive finite +f64 NaNf64
f64 NaNf64 f64 f64 f64 f64 f64 NaNf64
negative finite +f64 (x.value – y.value)f64 x x (x.value – y.value)f64 f64 NaNf64
–zerof64 +f64 (–y.value)f64 +zerof64 –zerof64 (–y.value)f64 f64 NaNf64
+zerof64 +f64 (–y.value)f64 +zerof64 +zerof64 (–y.value)f64 f64 NaNf64
positive finite +f64 (x.value – y.value)f64 x x (x.value – y.value)f64 f64 NaNf64
+f64 +f64 +f64 +f64 +f64 +f64 NaNf64 NaNf64
NaNf64 NaNf64 NaNf64 NaNf64 NaNf64 NaNf64 NaNf64 NaNf64
float64Multiply(xFloat64yFloat64): Float64
x y
f64 negative finite –zerof64 +zerof64 positive finite +f64 NaNf64
f64 +f64 +f64 NaNf64 NaNf64 f64 f64 NaNf64
negative finite +f64 (x.value  y.value)f64 +zerof64 –zerof64 (x.value  y.value)f64 f64 NaNf64
–zerof64 NaNf64 +zerof64 +zerof64 –zerof64 –zerof64 NaNf64 NaNf64
+zerof64 NaNf64 –zerof64 –zerof64 +zerof64 +zerof64 NaNf64 NaNf64
positive finite f64 (x.value  y.value)f64 –zerof64 +zerof64 (x.value  y.value)f64 +f64 NaNf64
+f64 f64 f64 NaNf64 NaNf64 +f64 +f64 NaNf64
NaNf64 NaNf64 NaNf64 NaNf64 NaNf64 NaNf64 NaNf64 NaNf64
float64Divide(xFloat64yFloat64): Float64
x y
f64 negative finite –zerof64 +zerof64 positive finite +f64 NaNf64
f64 NaNf64 +f64 +f64 f64 f64 NaNf64 NaNf64
negative finite +zerof64 (x.value / y.value)f64 +f64 f64 (x.value / y.value)f64 –zerof64 NaNf64
–zerof64 +zerof64 +zerof64 NaNf64 NaNf64 –zerof64 –zerof64 NaNf64
+zerof64 –zerof64 –zerof64 NaNf64 NaNf64 +zerof64 +zerof64 NaNf64
positive finite –zerof64 (x.value / y.value)f64 f64 +f64 (x.value / y.value)f64 +zerof64 NaNf64
+f64 NaNf64 f64 f64 +f64 +f64 NaNf64 NaNf64
NaNf64 NaNf64 NaNf64 NaNf64 NaNf64 NaNf64 NaNf64 NaNf64
float64Remainder(xFloat64yFloat64): Float64
x y
f64, +f64 positive or negative finite –zerof64, +zerof64 NaNf64
f64 NaNf64 NaNf64 NaNf64 NaNf64
negative finite x float64Negate(float64Remainder(float64Negate(x), y)) NaNf64 NaNf64
–zerof64 –zerof64 –zerof64 NaNf64 NaNf64
+zerof64 +zerof64 +zerof64 NaNf64 NaNf64
positive finite x (x.value – |y.value|x.value/|y.value|)f64 NaNf64 NaNf64
+f64 NaNf64 NaNf64 NaNf64 NaNf64
NaNf64 NaNf64 NaNf64 NaNf64 NaNf64

Note that float64Remainder(float64Negate(x), y) always produces the same result as float64Negate(float64Remainder(xy)). Also, float64Remainder(xfloat64Negate(y)) always produces the same result as float64Remainder(xy).

Procedures

A procedure is a function that receives zero or more arguments, performs computations, and optionally returns a result. Procedures may perform side effects. In this document the word procedure is used to refer to internal algorithms; the word function is used to refer to the programmer-visible function ECMAScript construct.

A procedure is denoted as:

proc f(param1T1, ... , paramnTn): T
step1;
step2;
... ;
stepm
end proc;

If the procedure does not return a value, the : T on the first line is omitted.

f is the procedure’s name, param1 through paramn are the procedure’s parameters, T1 through Tn are the parameters’ respective semantic domains, T is the semantic domain of the procedure’s result, and step1 through stepm describe the procedure’s computation steps, which may produce side effects and/or return a result. If T is omitted, the procedure does not return a result. When the procedure is called with argument values v1 through vn, the procedure’s steps are performed and the result, if any, returned to the caller.

A procedure’s steps can refer to the parameters param1 through paramn; each reference to a parameter parami evaluates to the corresponding argument value vi. Procedure parameters are statically scoped. Arguments are passed by value.

Operations

The only operation done on a procedure f is calling it using the f(arg1, ..., argn) syntax. f is computed first, followed by the argument expressions arg1 through argn, in left-to-right order. If the result of computing f or any of the argument expressions throws an exception e, then the call immediately propagates e without computing any following argument expressions. Otherwise, f is invoked using the provided arguments and the resulting value, if any, returned to the caller.

Procedures are never compared using =, , or any of the other comparison operators.

Semantic Domains of Procedures

The semantic domain of procedures that take n parameters in semantic domains T1 through Tn respectively and produce a result in semantic domain T is written as T1  T2  ...  Tn  T. If n = 0, this semantic domain is written as ()  T. If the procedure does not produce a result, the semantic domain of procedures is written either as T1  T2  ...  Tn  () or as ()  ().

Computation Steps

Computation steps in procedures are described using a mixture of English and formal notation. The various kinds of formal steps are described in this section. Multiple steps are separated by semicolons and performed in order unless an earlier step exits via a return or propagates an exception.

Informal steps state invariants and provide comments.

nothing

A nothing step performs no operation.

note Comment

A note step performs no operation. It provides an informative comment about the algorithm. If Comment is an expression, then the note step is an informative comment that asserts that the expression, if evaluated at this point, would be guaranteed to evaluate to true.

expression

A computation step may consist of an expression. The expression is computed and its value, if any, ignored.

vT  expression
v  expression

An assignment step is indicated using the assignment operator . This step computes the value of expression and assigns the result to the temporary variable or mutable global v. If this is the first time the temporary variable is referenced in a procedure, the variable’s semantic domain T is listed; any value stored in v is guaranteed to be a member of the semantic domain T.

vT

This step declares v to be a temporary variable with semantic domain T without assigning anything to the variable. v will not be read unless some other step first assigns a value to it.

Action[nonterminali expression

Inside an action, the assignment operator can also be used to define the result of another action Action[nonterminali] applied to the current expansion of nonterminali, which must appear on the current production’s left or right side. Such an assignment is done only when the value of Action[nonterminali] is not defined explicitly via an action. The value of Action[nonterminali] is set at most once and never modified afterwards. If the same nonterminal is expanded several times while parsing a source program, all such expansions are treated independently.

Temporary variables are local to the procedures that define them (including any nested procedures). Each time a procedure is called it gets a new set of temporary variables.

a.label  expression

This form of assignment sets the value of field label of record a to the value of expression.

if expression1 then stepstep; ...; step
elsif expression2 then stepstep; ...; step
...
elsif expressionn then stepstep; ...; step
else stepstep; ...; step
end if

An if step computes expression1, which will evaluate to either true or false. If it is true, the first list of steps is performed. Otherwise, expression2 is computed and tested, and so on. If no expression evaluates to true, the list of steps following the else is performed. The else clause may be omitted, in which case no action is taken when no expression evaluates to true.

case expression of
T1 do stepstep; ...; step;
T2 do stepstep; ...; step;
...;
Tn do stepstep; ...; step
else stepstep; ...; step
end case

A case step computes expression, which will evaluate to a value v. If v  T1, then the first list of steps is performed. Otherwise, if v  T2, then the second list of steps is performed, and so on. If v is not a member of any Ti, the list of steps following the else is performed. The else clause may be omitted, in which case v will always be a member of some Ti.

while expression do
step;
step;
...;
step
end while

A while step computes expression, which will evaluate to either true or false. If it is false, no action is taken. If it is true, the list of steps is performed and then expression is computed and tested again. This repeats until expression returns true (or until the procedure exits via a return or an exception is propagated out).

for each x  expression do
step;
step;
...;
step
end for each

A for each step computes expression, which will evaluate to either a set or a list A. The list of steps is performed repeatedly with variable x bound to each element of A. If A is a list, x is bound to each of its elements in order; if A is a set, the order in which x is bound to its elements is arbitrary. The repetition ends after x has been bound to all elements of A (or when either the procedure exits via a return or an exception is propagated out).

return expression

A return step computes expression to obtain a value v and returns from the enclosing procedure with the result v. No further steps in the enclosing procedure are performed. The expression may be omitted, in which case the enclosing procedure returns with no result.

Exceptions

throw expression

A throw step computes expression to obtain a value v and begins propagating exception v outwards, exiting partially performed steps and procedure calls until the exception is caught by a catch step. Unless the enclosing procedure catches this exception, no further steps in the enclosing procedure are performed.

try
step;
step;
...;
step
catch vT do
step;
step;
...;
step
end try

A try step performs the first list of steps. If they complete normally (or if they return), then the try step is done. If any of the steps propagates out an exception e, then if e  T, then exception e stops propagating, variable v is bound to the value e, and the second list of steps is performed. If e  T, then exception e keeps propagating out.

A try step does not intercept exceptions that may be propagated out of its second list of steps.

Nested Procedures

An inner proc may be nested as a step inside an outer proc. In this case the inner procedure is a closure and can access the parameters and temporaries of the outer procedure.

Semantic Actions

Semantic actions tie the grammar and the semantics together. A semantic action ascribes semantic meaning to a grammar production.

To illustrate the use of semantic actions, we shall look at an example, followed by a description of the notation for specifying semantic actions.

Example

Consider the following grammar, with the start nonterminal Numeral:

Digit  0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9
Digits 
   Digit
Numeral 
   Digits
|  Digits # Digits

This grammar defines the syntax of an acceptable input: “37”, “33#4” and “30#2” are acceptable syntactically, while “1a” is not. However, the grammar does not indicate what these various inputs mean. That is the job of the semantics, which are defined in terms of actions on the parse tree of grammar rule expansions. Consider the following sample set of actions defined on this grammar, with a starting Numeral action called (in this example) Value:

tag syntaxError;
SemanticException = {syntaxError};
Value[Digit]: Integer = digitValue(Digit);
DecimalValue[Digits]: Integer;
DecimalValue[Digits  Digit] = Value[Digit];
DecimalValue[Digits0  Digits1 Digit] = 10DecimalValue[Digits1] + Value[Digit];
proc BaseValue[Digits] (baseInteger): Integer
[Digits  Digitdo
dInteger  Value[Digit];
if d < base then return d else throw syntaxError end if;
[Digits0  Digits1 Digitdo
dInteger  Value[Digit];
if d < base then return baseBaseValue[Digits1](base) + d
else throw syntaxError
end if
end proc;
Value[Numeral]: Integer;
Value[Numeral  Digits] = DecimalValue[Digits];
Value[Numeral  Digits1 # Digits2]
begin
baseInteger  DecimalValue[Digits2];
if base  2 and base  10 then return BaseValue[Digits1](base)
else throw syntaxError
end if
end;

Action names are written in violet cursive type. The definition

Value[Numeral]: Integer;

states that the action Value can be applied to any expansion of the nonterminal Numeral, and the result is an Integer. This action either maps an input to an integer or throws an exception. The code above throws the exception syntaxError when presented with the input “30#2”.

There are two definitions of the Value action on Numeral, one for each grammar production that expands Numeral:

Value[Numeral  Digits] = DecimalValue[Digits];
Value[Numeral  Digits1 # Digits2]
begin
baseInteger  DecimalValue[Digits2];
if base  2 and base  10 then return BaseValue[Digits1](base)
else throw syntaxError
end if
end;

Each definition of an action is allowed to perform actions on the terminals and nonterminals on the right side of the expansion. For example, Value applied to the first Numeral production (the one that expands Numeral into Digits) simply applies the DecimalValue action to the expansion of the nonterminal Digits and returns the result. On the other hand, Value applied to the second Numeral production (the one that expands Numeral into Digits # Digits) performs a computation using the results of the DecimalValue and BaseValue applied to the two expansions of the Digits nonterminals. In this case there are two identical nonterminals Digits on the right side of the expansion, so subscripts are used to indicate on which the actions DecimalValue and BaseValue are performed.

The definition

proc BaseValue[Digits] (baseInteger): Integer
[Digits  Digitdo
dInteger  Value[Digit];
if d < base then return d else throw syntaxError end if;
[Digits0  Digits1 Digitdo
dInteger  Value[Digit];
if d < base then return baseBaseValue[Digits1](base) + d
else throw syntaxError
end if
end proc;

states that the action BaseValue can be applied to any expansion of the nonterminal Digits, and the result is a procedure that takes one Integer argument base and returns an Integer. The procedure’s body is comprised of independent cases for each production that expands Digits. When the procedure is called, the case corresponding to the expansion of the nonterminal Digits is evaluated.

The Value action on Digit illustrates the direct use of a nonterminal in a semantic expression: digitValue(Digit). Using the nonterminal Digit in this way refers to the character into which the Digit grammar rule expands.

We can fully evaluate the semantics on our sample inputs to get the following results:

Input    Semantic Result
37 37
33#4 15
30#2 throw syntaxError

Abbreviated Actions

In some cases the all actions named A for a nonterminal N’s rule are repetitive, merely calling A on the nonterminals on the right side of the expansions of N in the grammar. In these cases the semantics of action A are abbreviated, as illustrated by the example below.

Given the grammar rule

Expression 
   Subexpression
|  Expression * Subexpression
|  Subexpression + Subexpression
|  this

the notation

Validate[Expression] (cxtContextenvEnvironment) propagates the call to Validate to nonterminals in the expansion of Expression.

is an abbreviation for the following:

proc Validate[Expression] (cxtContextenvEnvironment)
[Expression  Subexpressiondo Validate[Subexpression](cxtenv);
[Expression0  Expression1 * Subexpressiondo
Validate[Expression1](cxtenv);
Validate[Subexpression](cxtenv);
[Expression  Subexpression1 + Subexpression2do
Validate[Subexpression1](cxtenv);
Validate[Subexpression2](cxtenv);
[Expression  thisdo nothing
end proc;

Note that:

  • The expanded calls to Validate get the same arguments cxt and env passed in to the call to Validate on Expression.
  • When an expansion of Expression has more than one nonterminal on its right side, Validate is called on all of the nonterminals in left-to-right order.
  • When an expansion of Expression has no nonterminals on its right side, Validate does nothing.

The propagation notation is also used in when the actions return a value. In this case each expansion must have exactly one nonterminal. For example, given the grammar rule

Id 
   SimpleId
|  ComplexId

the notation

Eval[Id] (envEnvironmentphasePhase): Multiname propagates the call to Eval to nonterminals in the expansion of Id.

is an abbreviation for the following:

proc Eval[Id] (envEnvironmentphasePhase): Multiname
[Id  SimpleIddo return Eval[SimpleId](envphase);
[Id  ComplexIddo return Eval[ComplexId](envphase)
end proc;

Semantic Definition Summary

The following notation is used to define top-level semantic entities:

Name = expression;

This notation defines Name to be a shorthand for the semantic domain expression. In the HTML version of the semantics, each use of Name is linked back to this definition.

nameT = expression;

This notation defines name to be a constant value given by the result of computing expression. The value is guaranteed to be a member of the semantic domain T. In the HTML version of the semantics, each use of name is linked back to this definition.

nameT  expression;

This notation defines name to be a mutable global value. Its initial value is the result of computing expression, but it may be subsequently altered using an assignment. The value is guaranteed to be a member of the semantic domain T. In the HTML version of the semantics, each use of name is linked back to this definition.

proc f(param1T1, ... , paramnTn): T
step1;
step2;
... ;
stepm
end proc;

This notation defines f to be a procedure.

tag name;

This notation defines a tag.

tuple Name
label1T1,
... ,
labelnTn
end tuple;

This notation defines a tuple.

record Name
label1T1,
... ,
labelnTn
end record;

This notation defines a record.

Action[nonterminal]: T;

This notation states that action Action can be performed on nonterminal nonterminal and returns a value that is a member of the semantic domain T. The action’s value is either defined using the notation Action[nonterminal  expansion] = expression below or set as a side effect of computing another action via an action assignment.

Action[nonterminal  expansion] = expression;

This notation specifies the value that action Action on nonterminal nonterminal computes in the case where nonterminal nonterminal expands to the given expansion. expansion can contain zero or more terminals and nonterminals (as well as other notations allowed on the right side of a grammar production). Furthermore, the terminals and nonterminals of expansion can be subscripted to allow them to be unambiguously referenced by action references or nonterminal references inside expression.

Action[nonterminal  expansion]: T = expression;

This notation combines the above two — it specifies the semantic domain of the action as well as its value.

Action[nonterminal  expansion]
begin
step1;
step2;
... ;
stepm
end;

This notation is used when the computation of the action is too complex for an expression. Here the steps to compute the action are listed as step1 through stepm. A return step produces the value of the action.

proc Action[nonterminal  expansion] (param1T1, ... , paramnTn): T
step1;
step2;
... ;
stepm
end proc;

This notation is used only when Action returns a procedure when applied to nonterminal nonterminal with a single expansion expansion. Here the steps of the procedure are listed as step1 through stepm.

proc Action[nonterminal] (param1T1, ... , paramnTn): T
[nonterminal  expansion1do
step;
... ;
step;
[nonterminal  expansion2do
step;
... ;
step;
...;
[nonterminal  expansionndo
step;
... ;
step
end proc;

This notation is used only when Action returns a procedure when applied to nonterminal nonterminal with several expansions expansion1 through expansionn. The procedure is comprised of a series of cases, one for each expansion. Only the steps corresponding to the expansion found by the grammar parser used are evaluated.

Action[nonterminal] (param1T1, ... , paramnTn) propagates the call to Action to every nonterminal in the expansion of nonterminal.

This notation is an abbreviation stating that calling Action on nonterminal causes Action to be called with the same arguments on every nonterminal on the right side of the appropriate expansion of nonterminal.


ECMAScript 4 Netscape Proposal
Formal Description
Stages
previousupnext

Tuesday, October 15, 2002

The source code is processed in the following stages:

  1. If necessary, convert the source code into the Unicode UTF-16 format, normalized form C.
  2. Remove any Unicode format control characters (category Cf) from the source code.
  3. Simultaneously split the source code into input elements using the lexical grammar and semantics and parse it using the syntactic grammar to obtain a parse tree P.
  4. Evaluate P using the syntactic semantics by computing the action Eval on it.

Lexing and Parsing

Processing stage 3 is done as follows:

  1. Let inputElements be an empty array of input elements (syntactic grammar terminals and line breaks).
  2. Let input be the input sequence of Unicode characters. Append a special placeholder End to the end of input.
  3. Let state be a variable that holds one of the constants re, div, or num. Initialize it to re.
  4. Apply the lexical grammar to parse the longest possible prefix of input. Use the start symbol NextInputElementre, NextInputElementdiv, or NextInputElementnum depending on whether state is re, div, or num, respectively. The result of the parse should be a lexical grammar parse tree T. If the parse failed, return a syntax error.
  5. Compute the action InputElement on T to obtain an InputElement e.
  6. If e is the endOfInput input element, go to step 15.
  7. Remove the characters matched by T from input, leaving only the yet-unlexed suffix of input.
  8. Interpret e as a syntactic grammar terminal or line break as follows:
    • A lineBreak is interpreted as a line break, which is not a terminal itself but indicates one or more line breaks between two terminals. It prevents the syntactic grammar from matching any productions that have a [no line break] annotation in the place where the lineBreak occurred.
    • An Identifier s is interpreted as the terminal Identifier. Applying the semantic action Name to the Identifier returns the String value s.name.
    • A Keyword s is interpreted as the reserved word, future reserved word, or non-reserved word terminal corresponding to the Keyword’s String s.
    • A Punctuator s is interpreted as the punctuation token or future punctuation token terminal corresponding to the Punctuator’s String s.
    • A NumberToken x is interpreted as the terminal Number. Applying the semantic action Value to the Number returns the GeneralNumber value x.value.
    • A negatedMinLong, which results from a numeric long token with the value 263, is interpreted as the terminal NegatedMinLong.
    • A StringToken s is interpreted as the terminal String. Applying the semantic action Value to the String returns the String value s.value.
    • A RegularExpression z is interpreted as the terminal RegularExpression.
  9. Append the resulting terminal or line break to the end of the inputElements array.
  10. If the inputElements array forms a valid prefix of the context-free language defined by the syntactic grammar, go to step 13.
  11. If is not a lineBreak but the previous element of the inputElements array is a lineBreak, then insert a VirtualSemicolon terminal between that lineBreak and in the inputElements array.
  12. If the inputElements array still does not form a valid prefix of the context-free language defined by the syntactic grammar, signal a syntax error and stop.
  13. If is a Number or NegatedMinLong, then set state to num. Otherwise, if the inputElements array followed by the terminal / forms a valid prefix of the context-free language defined by the syntactic grammar, then set state to div; otherwise, set state to re.
  14. Go to step 4.
  15. If the inputElements array does not form a valid sentence of the context-free language defined by the syntactic grammar, signal a syntax error and stop.
  16. Return the parse tree obtained by the syntactic grammar’s derivation of the sentence formed by the inputElements array.

ECMAScript 4 Netscape Proposal
Formal Description
Lexical Grammar
previousupnext

Monday, June 30, 2003

This LALR(1) grammar describes the lexical syntax of the ECMAScript 4 proposal. See also the description of the grammar notation.

This document is also available as a Word RTF file.

The lexer’s start symbols are: NextInputElementnum if the previous input element was a number; NextInputElementre if the previous input element was not a number and a / should be interpreted as a regular expression; and NextInputElementdiv if the previous input element was not a number and a / should be interpreted as a division or division-assignment operator.

In addition to the above, the start symbol StringNumericLiteral is used by the syntactic semantics for string-to-number conversions and the start symbol StringDecimalLiteral is used by the syntactic semantics for implementing the parseFloat function.

Unicode Character Classes

UnicodeCharacter  Any Unicode character
UnicodeInitialAlphabetic  Any character in category Lu (uppercase letter), Ll (lowercase letter), Lt (titlecase letter), Lm (modifier letter), Lo (other letter), or Nl (letter number) in the Unicode Character Database
UnicodeAlphanumeric  Any character in category Lu (uppercase letter), Ll (lowercase letter), Lt (titlecase letter), Lm (modifier letter), Lo (other letter), Nd (decimal number), Nl (letter number), Mn (non-spacing mark), Mc (combining spacing mark), or Pc (connector punctuation) in the Unicode Character Database
WhiteSpaceCharacter 
   «TAB» | «VT» | «FF» | «SP» | «u00A0»
|  «u2000» | «u2001» | «u2002» | «u2003» | «u2004» | «u2005» | «u2006» | «u2007»
|  «u2008» | «u2009» | «u200A» | «u200B»
|  «u3000»
LineTerminator  «LF» | «CR» | «u0085» | «u2028» | «u2029»
ASCIIDigit  0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9

Comments

LineComment  / / LineCommentCharacters
LineCommentCharacters 
   «empty»
NonTerminator  UnicodeCharacter except LineTerminator
SingleLineBlockComment  / * BlockCommentCharacters * /
BlockCommentCharacters 
   «empty»
PreSlashCharacters 
   «empty»
NonTerminatorOrSlash  NonTerminator except /
NonTerminatorOrAsteriskOrSlash  NonTerminator except * | /
MultiLineBlockComment  / * MultiLineBlockCommentCharacters BlockCommentCharacters * /
MultiLineBlockCommentCharacters 

White Space

WhiteSpace 
   «empty»

Line Breaks

LineBreak 
LineBreaks 
   LineBreak

Input Elements

  {redivnum}
NextInputElementre  WhiteSpace InputElementre
NextInputElementdiv  WhiteSpace InputElementdiv
NextInputElementnum  [lookahead{ContinuingIdentifierCharacter\}] WhiteSpace InputElementdiv
InputElementre 
InputElementdiv 
EndOfInput 
   End
|  LineComment End

Keywords and Identifiers

IdentifierOrKeyword  IdentifierName
NullEscapes 
NullEscape  \ _
InitialIdentifierCharacterOrEscape 
|  \ HexEscape
InitialIdentifierCharacter  UnicodeInitialAlphabetic | $ | _
ContinuingIdentifierCharacterOrEscape 
|  \ HexEscape
ContinuingIdentifierCharacter  UnicodeAlphanumeric | $ | _

Punctuators

Punctuator 
   !
|  ! =
|  ! = =
|  %
|  % =
|  &
|  & &
|  & & =
|  & =
|  (
|  )
|  *
|  * =
|  +
|  + +
|  + =
|  ,
|  -
|  - -
|  - =
|  .
|  . . .
|  :
|  : :
|  ;
|  <
|  < <
|  < < =
|  < =
|  =
|  = =
|  = = =
|  >
|  > =
|  > >
|  > > =
|  > > >
|  > > > =
|  ?
|  [
|  ]
|  ^
|  ^ =
|  ^ ^
|  ^ ^ =
|  {
|  |
|  | =
|  | |
|  | | =
|  }
|  ~
DivisionPunctuator 
   / [lookahead{/*}]
|  / =

Numeric Literals

NumericLiteral 
   DecimalLiteralnoLeadingZeros
|  DecimalLiteralnoLeadingZeros LetterF
IntegerLiteral 
   DecimalIntegerLiteralnoLeadingZeros
LetterF  F | f
LetterL  L | l
LetterU  U | u
  {noLeadingZerosallowLeadingZeros}
DecimalLiteral 
   Mantissa
|  Mantissa LetterE SignedInteger
LetterE  E | e
Mantissa 
   DecimalIntegerLiteral
|  DecimalIntegerLiteral .
|  DecimalIntegerLiteral . Fraction
|  . Fraction
DecimalIntegerLiteralnoLeadingZeros 
   0
DecimalIntegerLiteralallowLeadingZeros  DecimalDigits
NonZeroDecimalDigits 
NonZeroDigit  1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9
Fraction  DecimalDigits
SignedInteger  OptionalSign DecimalDigits
OptionalSign 
   «empty»
|  +
|  -
DecimalDigits 
HexIntegerLiteral 
   0 LetterX HexDigit
LetterX  X | x
HexDigit  0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | A | B | C | D | E | F | a | b | c | d | e | f

String Literals

  {singledouble}
StringLiteral 
   ' StringCharssingle '
|  " StringCharsdouble "
StringChars 
   «empty»
|  StringChars StringChar
|  StringChars NullEscape
StringChar 
   LiteralStringChar
|  \ StringEscape
LiteralStringCharsingle  UnicodeCharacter except ' | \ | LineTerminator
LiteralStringChardouble  UnicodeCharacter except " | \ | LineTerminator
StringEscape 
IdentityEscape  NonTerminator except _ | UnicodeAlphanumeric
ControlEscape 
   b
|  f
|  n
|  r
|  t
|  v
ZeroEscape  0 [lookahead{ASCIIDigit}]
HexEscape 
   x HexDigit HexDigit

Regular Expression Literals

RegExpLiteral  RegExpBody RegExpFlags
RegExpFlags 
   «empty»
RegExpBody  / [lookahead{*}] RegExpChars /
RegExpChars 
RegExpChar 
OrdinaryRegExpChar  NonTerminator except \ | /

String-to-Number Conversion

SignedDecimalLiteral 
   OptionalSign DecimalLiteralallowLeadingZeros
|  OptionalSign I n f i n i t y
|  N a N
StringWhiteSpace 
   «empty»
WhiteSpaceOrLineTerminatorChar  WhiteSpaceCharacter | LineTerminator

parseFloat Conversion

StringDecimalLiteral  StringWhiteSpace SignedDecimalLiteral

ECMAScript 4 Netscape Proposal
Formal Description
Lexical Semantics
previousupnext

Monday, June 30, 2003

The lexical semantics describe the actions the lexer takes in order to transform an input stream of Unicode characters into a stream of tokens. For convenience, the lexical grammar is repeated here. See also the description of the semantic notation.

This document is also available as a Word RTF file.

The lexer’s start symbols are: NextInputElementnum if the previous input element was a number; NextInputElementre if the previous input element was not a number and a / should be interpreted as a regular expression; and NextInputElementdiv if the previous input element was not a number and a / should be interpreted as a division or division-assignment operator.

In addition to the above, the start symbol StringNumericLiteral is used by the syntactic semantics for string-to-number conversions and the start symbol StringDecimalLiteral is used by the syntactic semantics for implementing the parseFloat function.

Semantics

tag lineBreak;
tag endOfInput;
tuple Keyword
nameString
end tuple;
tuple Punctuator
nameString
end tuple;
tuple Identifier
nameString
end tuple;
tuple NumberToken
end tuple;
tag negatedMinLong;
tuple StringToken
valueString
end tuple;
tuple RegularExpression
bodyString,
flagsString
end tuple;
TokenKeyword  Punctuator  Identifier  NumberToken  {negatedMinLong StringToken  RegularExpression;
InputElement = {lineBreakendOfInput Token;
tag syntaxError;
tag rangeError;
SemanticException = {syntaxErrorrangeError};

Unicode Character Classes

Syntax

UnicodeCharacter  Any Unicode character
UnicodeInitialAlphabetic  Any character in category Lu (uppercase letter), Ll (lowercase letter), Lt (titlecase letter), Lm (modifier letter), Lo (other letter), or Nl (letter number) in the Unicode Character Database
UnicodeAlphanumeric  Any character in category Lu (uppercase letter), Ll (lowercase letter), Lt (titlecase letter), Lm (modifier letter), Lo (other letter), Nd (decimal number), Nl (letter number), Mn (non-spacing mark), Mc (combining spacing mark), or Pc (connector punctuation) in the Unicode Character Database
WhiteSpaceCharacter 
   «TAB» | «VT» | «FF» | «SP» | «u00A0»
|  «u2000» | «u2001» | «u2002» | «u2003» | «u2004» | «u2005» | «u2006» | «u2007»
|  «u2008» | «u2009» | «u200A» | «u200B»
|  «u3000»
LineTerminator  «LF» | «CR» | «u0085» | «u2028» | «u2029»
ASCIIDigit  0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9

Semantics

DecimalValue[ASCIIDigit]: Integer = digitValue(ASCIIDigit);

Comments

Syntax

LineComment  / / LineCommentCharacters
LineCommentCharacters 
   «empty»
NonTerminator  UnicodeCharacter except LineTerminator
SingleLineBlockComment  / * BlockCommentCharacters * /
BlockCommentCharacters 
   «empty»
PreSlashCharacters 
   «empty»
NonTerminatorOrSlash  NonTerminator except /
NonTerminatorOrAsteriskOrSlash  NonTerminator except * | /
MultiLineBlockComment  / * MultiLineBlockCommentCharacters BlockCommentCharacters * /
MultiLineBlockCommentCharacters 

White Space

Syntax

WhiteSpace 
   «empty»

Line Breaks

Syntax

LineBreak 
LineBreaks 
   LineBreak

Input Elements

Syntax

  {redivnum}
NextInputElementre  WhiteSpace InputElementre
NextInputElementdiv  WhiteSpace InputElementdiv
NextInputElementnum  [lookahead{ContinuingIdentifierCharacter\}] WhiteSpace InputElementdiv

Semantics

Lex[NextInputElement]: InputElement;
Lex[NextInputElementre  WhiteSpace InputElementre] = Lex[InputElementre];
Lex[NextInputElementdiv  WhiteSpace InputElementdiv] = Lex[InputElementdiv];
Lex[NextInputElementnum  [lookahead{ContinuingIdentifierCharacter\}] WhiteSpace InputElementdiv] = Lex[InputElementdiv];

Syntax

InputElementre 
InputElementdiv 
EndOfInput 
   End
|  LineComment End

Semantics

Lex[InputElement]: InputElement;
Lex[InputElement  LineBreaks] = lineBreak;
Lex[InputElement  IdentifierOrKeyword] = Lex[IdentifierOrKeyword];
Lex[InputElement  Punctuator] = Lex[Punctuator];
Lex[InputElementdiv  DivisionPunctuator] = Lex[DivisionPunctuator];
Lex[InputElement  NumericLiteral] = Lex[NumericLiteral];
Lex[InputElement  StringLiteral] = Lex[StringLiteral];
Lex[InputElementre  RegExpLiteral] = Lex[RegExpLiteral];
Lex[InputElement  EndOfInput] = endOfInput;

Keywords and Identifiers

Syntax

IdentifierOrKeyword  IdentifierName

Semantics

Lex[IdentifierOrKeyword  IdentifierName]: InputElement
begin
idString  Lex[IdentifierName];
if id  {“abstract”, “as”, “break”, “case”, “catch”, “class”, “const”, “continue”, “debugger”, “default”, “delete”, “do”, “else”, “enum”, “export”, “extends”, “false”, “finally”, “for”, “function”, “get”, “goto”, “if”, “implements”, “import”, “in”, “instanceof”, “interface”, “is”, “namespace”, “native”, “new”, “null”, “package”, “private”, “protected”, “public”, “return”, “set”, “super”, “switch”, “synchronized”, “this”, “throw”, “throws”, “transient”, “true”, “try”, “typeof”, “use”, “var”, “volatile”, “while”, “with”} and not ContainsEscapes[IdentifierNamethen
return Keywordnameid
else return Identifiernameid
end if
end;

Syntax

NullEscapes 
NullEscape  \ _
InitialIdentifierCharacterOrEscape 
|  \ HexEscape
InitialIdentifierCharacter  UnicodeInitialAlphabetic | $ | _
ContinuingIdentifierCharacterOrEscape 
|  \ HexEscape
ContinuingIdentifierCharacter  UnicodeAlphanumeric | $ | _

Semantics

ContainsEscapes[IdentifierName]: Boolean;
ContainsEscapes[IdentifierName  InitialIdentifierCharacterOrEscape] = ContainsEscapes[InitialIdentifierCharacterOrEscape];
ContainsEscapes[IdentifierName  NullEscapes InitialIdentifierCharacterOrEscape] = true;
ContainsEscapes[IdentifierName0  IdentifierName1 ContinuingIdentifierCharacterOrEscape] = ContainsEscapes[IdentifierName1or ContainsEscapes[ContinuingIdentifierCharacterOrEscape];
ContainsEscapes[IdentifierName  IdentifierName NullEscape] = true;
Lex[InitialIdentifierCharacterOrEscape  \ HexEscape]
begin
chChar16  Lex[HexEscape];
if the nonterminal InitialIdentifierCharacter can expand into [ch] then
return ch
else throw syntaxError
end if
end;
ContainsEscapes[InitialIdentifierCharacterOrEscape  InitialIdentifierCharacter] = false;
ContainsEscapes[InitialIdentifierCharacterOrEscape  \ HexEscape] = true;
Lex[ContinuingIdentifierCharacterOrEscape  \ HexEscape]
begin
chChar16  Lex[HexEscape];
if the nonterminal ContinuingIdentifierCharacter can expand into [ch] then
return ch
else throw syntaxError
end if
end;
ContainsEscapes[ContinuingIdentifierCharacterOrEscape  ContinuingIdentifierCharacter] = false;
ContainsEscapes[ContinuingIdentifierCharacterOrEscape  \ HexEscape] = true;

Punctuators

Syntax

Punctuator 
   !
|  ! =
|  ! = =
|  %
|  % =
|  &
|  & &
|  & & =
|  & =
|  (
|  )
|  *
|  * =
|  +
|  + +
|  + =
|  ,
|  -
|  - -
|  - =
|  .
|  . . .
|  :
|  : :
|  ;
|  <
|  < <
|  < < =
|  < =
|  =
|  = =
|  = = =
|  >
|  > =
|  > >
|  > > =
|  > > >
|  > > > =
|  ?
|  [
|  ]
|  ^
|  ^ =
|  ^ ^
|  ^ ^ =
|  {
|  |
|  | =
|  | |
|  | | =
|  }
|  ~
DivisionPunctuator 
   / [lookahead{/*}]
|  / =

Semantics

Lex[Punctuator  !] = Punctuatorname: “!;
Lex[Punctuator  ! =] = Punctuatorname: “!=;
Lex[Punctuator  ! = =] = Punctuatorname: “!==;
Lex[Punctuator  %] = Punctuatorname: “%;
Lex[Punctuator  % =] = Punctuatorname: “%=;
Lex[Punctuator  &] = Punctuatorname: “&;
Lex[Punctuator  & &] = Punctuatorname: “&&;
Lex[Punctuator  & & =] = Punctuatorname: “&&=;
Lex[Punctuator  & =] = Punctuatorname: “&=;
Lex[Punctuator  (] = Punctuatorname: “(;
Lex[Punctuator  )] = Punctuatorname: “);
Lex[Punctuator  *] = Punctuatorname: “*;
Lex[Punctuator  * =] = Punctuatorname: “*=;
Lex[Punctuator  +] = Punctuatorname: “+;
Lex[Punctuator  + +] = Punctuatorname: “++;
Lex[Punctuator  + =] = Punctuatorname: “+=;
Lex[Punctuator  ,] = Punctuatorname: “,;
Lex[Punctuator  -] = Punctuatorname: “-;
Lex[Punctuator  - -] = Punctuatorname: “--;
Lex[Punctuator  - =] = Punctuatorname: “-=;
Lex[Punctuator  .] = Punctuatorname: “.;
Lex[Punctuator  . . .] = Punctuatorname: “...;
Lex[Punctuator  :] = Punctuatorname: “:;
Lex[Punctuator  : :] = Punctuatorname: “::;
Lex[Punctuator  ;] = Punctuatorname: “;;
Lex[Punctuator  <] = Punctuatorname: “<;
Lex[Punctuator  < <] = Punctuatorname: “<<;
Lex[Punctuator  < < =] = Punctuatorname: “<<=;
Lex[Punctuator  < =] = Punctuatorname: “<=;
Lex[Punctuator  =] = Punctuatorname: “=;
Lex[Punctuator  = =] = Punctuatorname: “==;
Lex[Punctuator  = = =] = Punctuatorname: “===;
Lex[Punctuator  >] = Punctuatorname: “>;
Lex[Punctuator  > =] = Punctuatorname: “>=;
Lex[Punctuator  > >] = Punctuatorname: “>>;
Lex[Punctuator  > > =] = Punctuatorname: “>>=;
Lex[Punctuator  > > >] = Punctuatorname: “>>>;
Lex[Punctuator  > > > =] = Punctuatorname: “>>>=;
Lex[Punctuator  ?] = Punctuatorname: “?;
Lex[Punctuator  [] = Punctuatorname: “[;
Lex[Punctuator  ]] = Punctuatorname: “];
Lex[Punctuator  ^] = Punctuatorname: “^;
Lex[Punctuator  ^ =] = Punctuatorname: “^=;
Lex[Punctuator  ^ ^] = Punctuatorname: “^^;
Lex[Punctuator  ^ ^ =] = Punctuatorname: “^^=;
Lex[Punctuator  {] = Punctuatorname: “{;
Lex[Punctuator  |] = Punctuatorname: “|;
Lex[Punctuator  | =] = Punctuatorname: “|=;
Lex[Punctuator  | |] = Punctuatorname: “||;
Lex[Punctuator  | | =] = Punctuatorname: “||=;
Lex[Punctuator  }] = Punctuatorname: “};
Lex[Punctuator  ~] = Punctuatorname: “~;
Lex[DivisionPunctuator  / [lookahead{/*}]] = Punctuatorname: “/;
Lex[DivisionPunctuator  / =] = Punctuatorname: “/=;

Numeric Literals

Syntax

NumericLiteral 
   DecimalLiteralnoLeadingZeros
|  DecimalLiteralnoLeadingZeros LetterF
IntegerLiteral 
   DecimalIntegerLiteralnoLeadingZeros
LetterF  F | f
LetterL  L | l
LetterU  U | u

Semantics

Lex[NumericLiteral  DecimalLiteralnoLeadingZeros] = NumberTokenvalue: (Lex[DecimalLiteralnoLeadingZeros])f64;
Lex[NumericLiteral  HexIntegerLiteral] = NumberTokenvalue: (Lex[HexIntegerLiteral])f64;
Lex[NumericLiteral  DecimalLiteralnoLeadingZeros LetterF] = NumberTokenvalue: (Lex[DecimalLiteralnoLeadingZeros])f32;
Lex[NumericLiteral  IntegerLiteral LetterL]
begin
iInteger  Lex[IntegerLiteral];
if i  263 – 1 then return NumberTokenvalueilong
elsif i = 263 then return negatedMinLong
else throw rangeError
end if
end;
Lex[NumericLiteral  IntegerLiteral LetterU LetterL]
begin
iInteger  Lex[IntegerLiteral];
if i  264 – 1 then return NumberTokenvalueiulong
else throw rangeError
end if
end;
Lex[IntegerLiteral  DecimalIntegerLiteralnoLeadingZeros] = Lex[DecimalIntegerLiteralnoLeadingZeros];
Lex[IntegerLiteral  HexIntegerLiteral] = Lex[HexIntegerLiteral];

Syntax

  {noLeadingZerosallowLeadingZeros}
DecimalLiteral 
   Mantissa
|  Mantissa LetterE SignedInteger
LetterE  E | e
Mantissa 
   DecimalIntegerLiteral
|  DecimalIntegerLiteral .
|  DecimalIntegerLiteral . Fraction
|  . Fraction
DecimalIntegerLiteralnoLeadingZeros 
   0
DecimalIntegerLiteralallowLeadingZeros  DecimalDigits
NonZeroDecimalDigits 
NonZeroDigit  1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9
Fraction  DecimalDigits

Semantics

Lex[DecimalLiteral]: Rational;
Lex[DecimalLiteral  Mantissa] = Lex[Mantissa];
Lex[DecimalLiteral  Mantissa LetterE SignedInteger] = Lex[Mantissa]10Lex[SignedInteger];
Lex[Mantissa]: Rational;
Lex[Mantissa  DecimalIntegerLiteral] = Lex[DecimalIntegerLiteral];
Lex[Mantissa  DecimalIntegerLiteral .] = Lex[DecimalIntegerLiteral];
Lex[Mantissa  DecimalIntegerLiteral . Fraction] = Lex[DecimalIntegerLiteral] + Lex[Fraction];
Lex[Mantissa  . Fraction] = Lex[Fraction];
Lex[DecimalIntegerLiteral]: Integer;
Lex[DecimalIntegerLiteralnoLeadingZeros  0] = 0;
Lex[DecimalIntegerLiteralnoLeadingZeros  NonZeroDecimalDigits] = Lex[NonZeroDecimalDigits];
Lex[DecimalIntegerLiteralallowLeadingZeros  DecimalDigits] = Lex[DecimalDigits];
Lex[NonZeroDecimalDigits  NonZeroDigit] = DecimalValue[NonZeroDigit];
Lex[NonZeroDecimalDigits0  NonZeroDecimalDigits1 ASCIIDigit] = 10Lex[NonZeroDecimalDigits1] + DecimalValue[ASCIIDigit];
Lex[Fraction  DecimalDigits]: Rational = Lex[DecimalDigits]/10NDigits[DecimalDigits];

Syntax

SignedInteger  OptionalSign DecimalDigits
OptionalSign 
   «empty»
|  +
|  -

Semantics

Lex[SignedInteger  OptionalSign DecimalDigits]: IntegerLex[OptionalSign]Lex[DecimalDigits];
Lex[OptionalSign]: {–1, 1};
Lex[OptionalSign  «empty»] = 1;
Lex[OptionalSign  +] = 1;
Lex[OptionalSign  -] = –1;

Syntax

DecimalDigits 

Semantics

Lex[DecimalDigits  ASCIIDigit] = DecimalValue[ASCIIDigit];
Lex[DecimalDigits0  DecimalDigits1 ASCIIDigit] = 10Lex[DecimalDigits1] + DecimalValue[ASCIIDigit];
NDigits[DecimalDigits  ASCIIDigit] = 1;
NDigits[DecimalDigits0  DecimalDigits1 ASCIIDigit] = NDigits[DecimalDigits1] + 1;

Syntax

HexIntegerLiteral 
   0 LetterX HexDigit
LetterX  X | x
HexDigit  0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | A | B | C | D | E | F | a | b | c | d | e | f

Semantics

Lex[HexIntegerLiteral  0 LetterX HexDigit] = HexValue[HexDigit];
Lex[HexIntegerLiteral0  HexIntegerLiteral1 HexDigit] = 16Lex[HexIntegerLiteral1] + HexValue[HexDigit];

String Literals

Syntax

  {singledouble}
StringLiteral 
   ' StringCharssingle '
|  " StringCharsdouble "

Semantics

Lex[StringLiteral  ' StringCharssingle '] = StringTokenvalueLex[StringCharssingle];
Lex[StringLiteral  " StringCharsdouble "] = StringTokenvalueLex[StringCharsdouble];

Syntax

StringChars 
   «empty»
|  StringChars StringChar
|  StringChars NullEscape
StringChar 
   LiteralStringChar
|  \ StringEscape
LiteralStringCharsingle  UnicodeCharacter except ' | \ | LineTerminator
LiteralStringChardouble  UnicodeCharacter except " | \ | LineTerminator

Semantics

Lex[StringChars]: String;
Lex[StringChars  «empty»] = “”;
Lex[StringChars0  StringChars1 StringChar] = Lex[StringChars1 [Lex[StringChar]];
Lex[StringChars0  StringChars1 NullEscape] = Lex[StringChars1];
Lex[StringChar]: Char16;
Lex[StringChar  LiteralStringChar] = LiteralStringChar;
Lex[StringChar  \ StringEscape] = Lex[StringEscape];

Syntax

StringEscape 
IdentityEscape  NonTerminator except _ | UnicodeAlphanumeric

Semantics

Lex[StringEscape  ControlEscape] = Lex[ControlEscape];
Lex[StringEscape  ZeroEscape] = Lex[ZeroEscape];
Lex[StringEscape  HexEscape] = Lex[HexEscape];
Lex[StringEscape  IdentityEscape] = IdentityEscape;

Syntax

ControlEscape 
   b
|  f
|  n
|  r
|  t
|  v

Semantics

Lex[ControlEscape  b] = ‘«BS»’;
Lex[ControlEscape  f] = ‘«FF»’;
Lex[ControlEscape  n] = ‘«LF»’;
Lex[ControlEscape  r] = ‘«CR»’;
Lex[ControlEscape  t] = ‘«TAB»’;
Lex[ControlEscape  v] = ‘«VT»’;

Syntax

ZeroEscape  0 [lookahead{ASCIIDigit}]

Semantics

Lex[ZeroEscape  0 [lookahead{ASCIIDigit}]]: Char16 = ‘«NUL»’;

Syntax

HexEscape 
   x HexDigit HexDigit

Semantics

Lex[HexEscape  x HexDigit1 HexDigit2] = integerToChar16(16HexValue[HexDigit1] + HexValue[HexDigit2]);
Lex[HexEscape  u HexDigit1 HexDigit2 HexDigit3 HexDigit4] = integerToChar16(4096HexValue[HexDigit1] + 256HexValue[HexDigit2] + 16HexValue[HexDigit3] + HexValue[HexDigit4]);
Lex[HexEscape  U HexDigit HexDigit HexDigit HexDigit HexDigit HexDigit HexDigit HexDigit] = ????;

Regular Expression Literals

Syntax

RegExpLiteral  RegExpBody RegExpFlags
RegExpFlags 
   «empty»
RegExpBody  / [lookahead{*}] RegExpChars /
RegExpChars 
RegExpChar 
OrdinaryRegExpChar  NonTerminator except \ | /

Semantics

Lex[RegExpLiteral  RegExpBody RegExpFlags]: TokenRegularExpressionbodyLex[RegExpBody], flagsLex[RegExpFlags];
Lex[RegExpFlags  «empty»] = “”;
Lex[RegExpFlags0  RegExpFlags1 ContinuingIdentifierCharacterOrEscape] = Lex[RegExpFlags1 [Lex[ContinuingIdentifierCharacterOrEscape]];
Lex[RegExpFlags0  RegExpFlags1 NullEscape] = Lex[RegExpFlags1];
Lex[RegExpBody  / [lookahead{*}] RegExpChars /]: String = Lex[RegExpChars];
Lex[RegExpChars  RegExpChar] = Lex[RegExpChar];
Lex[RegExpChars0  RegExpChars1 RegExpChar] = Lex[RegExpChars1 Lex[RegExpChar];
Lex[RegExpChar  OrdinaryRegExpChar] = [OrdinaryRegExpChar];
Lex[RegExpChar  \ NonTerminator] = [\’, NonTerminator];

String-to-Number Conversion

Syntax

SignedDecimalLiteral 
   OptionalSign DecimalLiteralallowLeadingZeros
|  OptionalSign I n f i n i t y
|  N a N
StringWhiteSpace 
   «empty»
WhiteSpaceOrLineTerminatorChar  WhiteSpaceCharacter | LineTerminator

Semantics

tag +zero;
tag –zero;
tag +;
tag ;
tag NaN;
ExtendedRational = Rational  {+zero–zero+NaN};
Lex[SignedDecimalLiteral  OptionalSign DecimalLiteralallowLeadingZeros] = combineWithSign(Lex[OptionalSign], Lex[DecimalLiteralallowLeadingZeros]);
Lex[SignedDecimalLiteral  OptionalSign I n f i n i t y] = Lex[OptionalSign] > 0 ? + : ;
Lex[SignedDecimalLiteral  N a N] = NaN;
proc combineWithSign(sign: {–1, 1}, qRational): ExtendedRational
if q  0 then return signq
elsif sign > 0 then return +zero
else return –zero
end if
end proc;

parseFloat Conversion

Syntax

StringDecimalLiteral  StringWhiteSpace SignedDecimalLiteral

Semantics


ECMAScript 4 Netscape Proposal
Formal Description
Regular Expression Grammar
previousupnext

Monday, June 9, 2003

This LR(1) grammar describes the regular expression syntax of the ECMAScript 4 proposal. See also the description of the grammar notation.

This document is also available as a Word RTF file.

Unicode Character Classes

UnicodeCharacter  Any Unicode character
UnicodeAlphanumeric  Any Unicode alphabetic or decimal digit character (includes ASCII 0-9, A-Z, and a-z)
LineTerminator  «LF» | «CR» | «u0085» | «u2028» | «u2029»

Regular Expression Definitions

Regular Expression Patterns

RegularExpressionPattern  Disjunction

Disjunctions

Disjunction 

Alternatives

Alternative 
   «empty»

Terms

Term 
   Assertion
|  Atom
Quantifier 
QuantifierPrefix 
   *
|  +
|  ?
|  { DecimalDigits }
|  { DecimalDigits , }
DecimalDigits 
DecimalDigit  0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9

Assertions

Assertion 
   ^
|  $
|  \ b
|  \ B

Atoms

Atom 
|  .
|  \ AtomEscape
|  ( Disjunction )
|  ( ? : Disjunction )
|  ( ? = Disjunction )
|  ( ? ! Disjunction )
PatternCharacter  UnicodeCharacter except ^ | $ | \ | . | * | + | ? | ( | ) | [ | ] | { | } | |

Escapes

NullEscape  \ _
AtomEscape 
CharacterEscape 
ControlLetter 
   A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P | Q | R | S | T | U | V | W | X | Y | Z
|  a | b | c | d | e | f | g | h | i | j | k | l | m | n | o | p | q | r | s | t | u | v | w | x | y | z
IdentityEscape  UnicodeCharacter except _ | UnicodeAlphanumeric
ControlEscape 
   f
|  n
|  r
|  t
|  v

Decimal Escapes

DecimalEscape  DecimalIntegerLiteral [lookahead{DecimalDigit}]
DecimalIntegerLiteral 
   0
NonZeroDecimalDigits 
NonZeroDigit  1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9

Hexadecimal Escapes

HexEscape 
   x HexDigit HexDigit
HexDigit  0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | A | B | C | D | E | F | a | b | c | d | e | f

Character Class Escapes

CharacterClassEscape 
   s
|  S
|  d
|  D
|  w
|  W

User-Specified Character Classes

CharacterClass 
   [ [lookahead{^}] ClassRanges ]
|  [ ^ ClassRanges ]
ClassRanges 
   «empty»
  {dashnoDash}
NonemptyClassRanges 
   ClassAtomdash
|  ClassAtom NonemptyClassRangesnoDash
|  ClassAtom - ClassAtomdash ClassRanges

Character Class Range Atoms

ClassAtom 
   ClassCharacter
|  \ ClassEscape
ClassCharacterdash  UnicodeCharacter except \ | ]
ClassCharacternoDash  ClassCharacterdash except -
ClassEscape 
|  b

ECMAScript 4 Netscape Proposal
Formal Description
Regular Expression Semantics
previousupnext

Monday, June 9, 2003

The regular expression semantics describe the actions the regular expression engine takes in order to transform a regular expression pattern into a function for matching against input strings. For convenience, the regular expression grammar is repeated here. See also the description of the semantic notation.

This document is also available as a Word RTF file.

Case-insensitive matches are not implemented in the semantics below.

Semantics

tag syntaxError;
SemanticException = {syntaxError};

Unicode Character Classes

Syntax

UnicodeCharacter  Any Unicode character
UnicodeAlphanumeric  Any Unicode alphabetic or decimal digit character (includes ASCII 0-9, A-Z, and a-z)
LineTerminator  «LF» | «CR» | «u0085» | «u2028» | «u2029»

Semantics

lineTerminatorsChar16{} = {‘«LF»’, ‘«CR»’, ‘«u2028»’, ‘«u2029»’};
reWhitespacesChar16{} = {‘«FF»’, ‘«LF»’, ‘«CR»’, ‘«TAB»’, ‘«VT»’, ‘ ’};
reDigitsChar16{} = {‘0’ ... ‘9’};
reWordCharactersChar16{} = {‘0’ ... ‘9’, ‘A’ ... ‘Z’, ‘a’ ... ‘z’, ‘_’};

Regular Expression Definitions

Semantics

tuple REInput
strString,
ignoreCaseBoolean,
multilineBoolean,
spanBoolean
end tuple;

Field str is the input string. ignoreCase, multiline, and span are the corresponding regular expression flags.

tag undefined;
Capture = String  {undefined};
tuple REMatch
endIndexInteger,
capturesCapture[]
end tuple;
tag failure;
REResult = REMatch  {failure};

A REMatch holds an intermediate state during the pattern-matching process. endIndex is the index of the next input character to be matched by the next component in a regular expression pattern. If we are at the end of the pattern, endIndex is one plus the index of the last matched input character. captures is a zero-based array of the strings captured so far by capturing parentheses.

Continuation = REMatch  REResult;

A Continuation is a function that attempts to match the remaining portion of the pattern against the input string, starting at the intermediate state given by its REMatch argument. If a match is possible, it returns a REMatch result that contains the final state; if no match is possible, it returns a failure result.

Matcher = REInput  REMatch  Continuation  REResult;

A Matcher is a function that attempts to match a middle portion of the pattern against the input string, starting at the intermediate state given by its REMatch argument. Since the remainder of the pattern heavily influences whether (and how) a middle portion will match, we must pass in a Continuation function that checks whether the rest of the pattern matched. If the continuation returns failure, the matcher function may call it repeatedly, trying various alternatives at pattern choice points.

The REInput parameter contains the input string and is merely passed down to subroutines.

A Integer Matcher is a function executed at the time the regular expression is compiled that returns a Matcher for a part of the pattern. The Integer parameter contains the number of capturing left parentheses seen so far in the pattern and is used to assign static, consecutive numbers to capturing parentheses.

proc characterSetMatcher(acceptanceSetChar16{}, invertBoolean): Matcher
proc m(tREInputxREMatchcContinuation): REResult
iInteger  x.endIndex;
sString  t.str;
if i = |sthen return failure
elsif s[i acceptanceSet xor invert then
return c(REMatchendIndexi + 1, capturesx.captures)
else return failure
end if
end proc;
return m
end proc;

characterSetMatcher returns a Matcher that matches a single input string character. If invert is false, the match succeeds if the character is a member of the acceptanceSet set of characters (possibly ignoring case). If invert is true, the match succeeds if the character is not a member of the acceptanceSet set of characters (possibly ignoring case).

proc characterMatcher(chChar16): Matcher
return characterSetMatcher({ch}, false)
end proc;

characterMatcher returns a Matcher that matches a single input string character. The match succeeds if the character is the same as ch (possibly ignoring case).

Regular Expression Patterns

Syntax

RegularExpressionPattern  Disjunction

Semantics

Execute[RegularExpressionPattern  Disjunction]: REInput  Integer  REResult
begin
m1Matcher  GenMatcher[Disjunction](0);
proc e(tREInputindexInteger): REResult
xREMatch  REMatchendIndexindex, capturesrepeat(undefinedCountParens[Disjunction]);
return m1(txsuccessContinuation)
end proc;
return e
end;
proc successContinuation(xREMatch): REResult
return x
end proc;

Disjunctions

Syntax

Disjunction 

Semantics

proc GenMatcher[Disjunction] (parenIndexInteger): Matcher
[Disjunction  Alternativedo return GenMatcher[Alternative](parenIndex);
[Disjunction0  Alternative | Disjunction1do
m1Matcher  GenMatcher[Alternative](parenIndex);
m2Matcher  GenMatcher[Disjunction1](parenIndex + CountParens[Alternative]);
proc m3(tREInputxREMatchcContinuation): REResult
yREResult  m1(txc);
case y of
REMatch do return y;
{failuredo return m2(txc)
end case
end proc;
return m3
end proc;
CountParens[Disjunction]: Integer;
CountParens[Disjunction  Alternative] = CountParens[Alternative];
CountParens[Disjunction0  Alternative | Disjunction1] = CountParens[Alternative] + CountParens[Disjunction1];

Alternatives

Syntax

Alternative 
   «empty»

Semantics

proc GenMatcher[Alternative] (parenIndexInteger): Matcher
[Alternative  «empty»] do
proc m(tREInputxREMatchcContinuation): REResult
return c(x)
end proc;
return m;
[Alternative0  Alternative1 Termdo
m1Matcher  GenMatcher[Alternative1](parenIndex);
m2Matcher  GenMatcher[Term](parenIndex + CountParens[Alternative1]);
proc m3(tREInputxREMatchcContinuation): REResult
proc d(yREMatch): REResult
return m2(tyc)
end proc;
return m1(txd)
end proc;
return m3
end proc;
CountParens[Alternative]: Integer;
CountParens[Alternative  «empty»] = 0;
CountParens[Alternative0  Alternative1 Term] = CountParens[Alternative1] + CountParens[Term];

Terms

Syntax

Term 
   Assertion
|  Atom

Semantics

proc GenMatcher[Term] (parenIndexInteger): Matcher
[Term  Assertiondo
proc m(tREInputxREMatchcContinuation): REResult
if TestAssertion[Assertion](txthen return c(xelse return failure end if
end proc;
return m;
[Term  Atomdo return GenMatcher[Atom](parenIndex);
[Term  Atom Quantifierdo
mMatcher  GenMatcher[Atom](parenIndex);
minInteger  Minimum[Quantifier];
maxLimit  Maximum[Quantifier];
greedyBoolean  Greedy[Quantifier];
if max  + then if max < min then throw syntaxError end if end if;
return repeatMatcher(mminmaxgreedyparenIndexCountParens[Atom])
end proc;
CountParens[Term]: Integer;
CountParens[Term  Assertion] = 0;
CountParens[Term  Atom] = CountParens[Atom];
CountParens[Term  Atom Quantifier] = CountParens[Atom];

Syntax

Quantifier 
QuantifierPrefix 
   *
|  +
|  ?
|  { DecimalDigits }
|  { DecimalDigits , }
DecimalDigits 
DecimalDigit  0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9

Semantics

tag +;
Limit = Integer  {+};
proc resetParens(xREMatchpIntegernParensInteger): REMatch
capturesCapture[]  x.captures;
iInteger  p;
while i < p + nParens do captures  captures[i \ undefined]; i  i + 1 end while;
return REMatchendIndexx.endIndexcapturescaptures
end proc;
proc repeatMatcher(bodyMatcher, minInteger, maxLimit, greedyBoolean, parenIndexInteger, nBodyParensInteger): Matcher
proc m(tREInputxREMatchcContinuation): REResult
if max = 0 then return c(xend if;
proc d(yREMatch): REResult
if min = 0 and y.endIndex = x.endIndex then return failure end if;
newMinInteger  min;
if min  0 then newMin  min – 1 end if;
newMaxLimit  max;
if max  + then newMax  max – 1 end if;
m2Matcher  repeatMatcher(bodynewMinnewMaxgreedyparenIndexnBodyParens);
return m2(tyc)
end proc;
xrREMatch  resetParens(xparenIndexnBodyParens);
if min  0 then return body(txrd)
elsif greedy then
zREResult  body(txrd);
case z of
REMatch do return z;
{failuredo return c(x)
end case
else
zREResult  c(x);
case z of
REMatch do return z;
{failuredo return body(txrd)
end case
end if
end proc;
return m
end proc;
Minimum[Quantifier]: Integer;
Minimum[Quantifier  QuantifierPrefix] = Minimum[QuantifierPrefix];
Minimum[Quantifier  QuantifierPrefix ?] = Minimum[QuantifierPrefix];
Maximum[Quantifier]: Limit;
Maximum[Quantifier  QuantifierPrefix] = Maximum[QuantifierPrefix];
Maximum[Quantifier  QuantifierPrefix ?] = Maximum[QuantifierPrefix];
Greedy[Quantifier]: Boolean;
Greedy[Quantifier  QuantifierPrefix] = true;
Greedy[Quantifier  QuantifierPrefix ?] = false;
Minimum[QuantifierPrefix  *] = 0;
Minimum[QuantifierPrefix  +] = 1;
Minimum[QuantifierPrefix  ?] = 0;
Minimum[QuantifierPrefix  { DecimalDigits }] = IntegerValue[DecimalDigits];
Minimum[QuantifierPrefix  { DecimalDigits , }] = IntegerValue[DecimalDigits];
Minimum[QuantifierPrefix  { DecimalDigits1 , DecimalDigits2 }] = IntegerValue[DecimalDigits1];
Maximum[QuantifierPrefix  *] = +;
Maximum[QuantifierPrefix  +] = +;
Maximum[QuantifierPrefix  ?] = 1;
Maximum[QuantifierPrefix  { DecimalDigits }] = IntegerValue[DecimalDigits];
Maximum[QuantifierPrefix  { DecimalDigits , }] = +;
Maximum[QuantifierPrefix  { DecimalDigits1 , DecimalDigits2 }] = IntegerValue[DecimalDigits2];
IntegerValue[DecimalDigits]: Integer;
IntegerValue[DecimalDigits  DecimalDigit] = DecimalValue[DecimalDigit];
IntegerValue[DecimalDigits0  DecimalDigits1 DecimalDigit] = 10IntegerValue[DecimalDigits1] + DecimalValue[DecimalDigit];

Assertions

Syntax

Assertion 
   ^
|  $
|  \ b
|  \ B

Semantics

proc TestAssertion[Assertion] (tREInputxREMatch): Boolean
[Assertion  ^do
return x.endIndex = 0 or (t.multiline and t.str[x.endIndex – 1]  lineTerminators);
[Assertion  $do
return x.endIndex = |t.stror (t.multiline and t.str[x.endIndex lineTerminators);
[Assertion  \ bdo return atWordBoundary(x.endIndext.str);
[Assertion  \ Bdo return not atWordBoundary(x.endIndext.str)
end proc;
proc atWordBoundary(iIntegersString): Boolean
return inWord(i – 1, sxor inWord(is)
end proc;
proc inWord(iIntegersString): Boolean
if i = –1 or i = |sthen return false else return s[i reWordCharacters end if
end proc;

Atoms

Syntax

Atom 
|  .
|  \ AtomEscape
|  ( Disjunction )
|  ( ? : Disjunction )
|  ( ? = Disjunction )
|  ( ? ! Disjunction )
PatternCharacter  UnicodeCharacter except ^ | $ | \ | . | * | + | ? | ( | ) | [ | ] | { | } | |

Semantics

proc GenMatcher[Atom] (parenIndexInteger): Matcher
[Atom  PatternCharacterdo return characterMatcher(PatternCharacter);
[Atom  .do
proc m1(tREInputxREMatchcContinuation): REResult
aChar16{}  t.span ? {} : lineTerminators;
m2Matcher  characterSetMatcher(atrue);
return m2(txc)
end proc;
return m1;
[Atom  NullEscapedo
proc m(tREInputxREMatchcContinuation): REResult
return c(x)
end proc;
return m;
[Atom  \ AtomEscapedo return GenMatcher[AtomEscape](parenIndex);
[Atom  CharacterClassdo
aChar16{}  AcceptanceSet[CharacterClass];
return characterSetMatcher(aInvert[CharacterClass]);
[Atom  ( Disjunction )do
m1Matcher  GenMatcher[Disjunction](parenIndex + 1);
proc m2(tREInputxREMatchcContinuation): REResult
proc d(yREMatch): REResult
refCapture  t.str[x.endIndex ... y.endIndex – 1];
updatedCapturesCapture[]  y.captures[parenIndex \ ref];
return c(REMatchendIndexy.endIndexcapturesupdatedCaptures)
end proc;
return m1(txd)
end proc;
return m2;
[Atom  ( ? : Disjunction )do return GenMatcher[Disjunction](parenIndex);
[Atom  ( ? = Disjunction )do
m1Matcher  GenMatcher[Disjunction](parenIndex);
proc m2(tREInputxREMatchcContinuation): REResult
yREResult  m1(txsuccessContinuation);
case y of
REMatch do return c(REMatchendIndexx.endIndexcapturesy.captures);
{failuredo return failure
end case
end proc;
return m2;
[Atom  ( ? ! Disjunction )do
m1Matcher  GenMatcher[Disjunction](parenIndex);
proc m2(tREInputxREMatchcContinuation): REResult
case m1(txsuccessContinuationof
REMatch do return failure;
{failuredo return c(x)
end case
end proc;
return m2
end proc;
CountParens[Atom]: Integer;
CountParens[Atom  PatternCharacter] = 0;
CountParens[Atom  .] = 0;
CountParens[Atom  NullEscape] = 0;
CountParens[Atom  \ AtomEscape] = 0;
CountParens[Atom  CharacterClass] = 0;
CountParens[Atom  ( Disjunction )] = CountParens[Disjunction] + 1;
CountParens[Atom  ( ? : Disjunction )] = CountParens[Disjunction];
CountParens[Atom  ( ? = Disjunction )] = CountParens[Disjunction];
CountParens[Atom  ( ? ! Disjunction )] = CountParens[Disjunction];

Escapes

Syntax

NullEscape  \ _
AtomEscape 

Semantics

proc GenMatcher[AtomEscape] (parenIndexInteger): Matcher
[AtomEscape  DecimalEscapedo
nInteger  EscapeValue[DecimalEscape];
if n = 0 then return characterMatcher(‘«NUL»’)
elsif n > parenIndex then throw syntaxError
else return backreferenceMatcher(n)
end if;
[AtomEscape  CharacterEscapedo
return characterMatcher(CharacterValue[CharacterEscape]);
[AtomEscape  CharacterClassEscapedo
return characterSetMatcher(AcceptanceSet[CharacterClassEscape], false)
end proc;
proc backreferenceMatcher(nInteger): Matcher
proc m(tREInputxREMatchcContinuation): REResult
refCapture  nthBackreference(xn);
case ref of
String do
iInteger  x.endIndex;
sString  t.str;
jInteger  i + |ref|;
if j  |sand s[i ... j – 1] = ref then
return c(REMatchendIndexjcapturesx.captures)
else return failure
end if;
{undefineddo return c(x)
end case
end proc;
return m
end proc;
proc nthBackreference(xREMatchnInteger): Capture
return x.captures[n – 1]
end proc;

Syntax

CharacterEscape 
ControlLetter 
   A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P | Q | R | S | T | U | V | W | X | Y | Z
|  a | b | c | d | e | f | g | h | i | j | k | l | m | n | o | p | q | r | s | t | u | v | w | x | y | z
IdentityEscape  UnicodeCharacter except _ | UnicodeAlphanumeric
ControlEscape 
   f
|  n
|  r
|  t
|  v

Semantics

CharacterValue[CharacterEscape]: Char16;
CharacterValue[CharacterEscape  ControlEscape] = CharacterValue[ControlEscape];
CharacterValue[CharacterEscape  c ControlLetter] = integerToChar16(bitwiseAnd(char16ToInteger(ControlLetter), 31));
CharacterValue[CharacterEscape  HexEscape] = CharacterValue[HexEscape];
CharacterValue[CharacterEscape  IdentityEscape] = IdentityEscape;
CharacterValue[ControlEscape]: Char16;
CharacterValue[ControlEscape  f] = ‘«FF»’;
CharacterValue[ControlEscape  n] = ‘«LF»’;
CharacterValue[ControlEscape  r] = ‘«CR»’;
CharacterValue[ControlEscape  t] = ‘«TAB»’;
CharacterValue[ControlEscape  v] = ‘«VT»’;

Decimal Escapes

Syntax

DecimalEscape  DecimalIntegerLiteral [lookahead{DecimalDigit}]
DecimalIntegerLiteral 
   0
NonZeroDecimalDigits 
NonZeroDigit  1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9

Semantics

EscapeValue[DecimalEscape  DecimalIntegerLiteral [lookahead{DecimalDigit}]]: IntegerIntegerValue[DecimalIntegerLiteral];
IntegerValue[DecimalIntegerLiteral  0] = 0;
IntegerValue[DecimalIntegerLiteral  NonZeroDecimalDigits] = IntegerValue[NonZeroDecimalDigits];
IntegerValue[NonZeroDecimalDigits]: Integer;
IntegerValue[NonZeroDecimalDigits  NonZeroDigit] = DecimalValue[NonZeroDigit];
IntegerValue[NonZeroDecimalDigits0  NonZeroDecimalDigits1 DecimalDigit] = 10IntegerValue[NonZeroDecimalDigits1] + DecimalValue[DecimalDigit];

Hexadecimal Escapes

Syntax

HexEscape 
   x HexDigit HexDigit
HexDigit  0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | A | B | C | D | E | F | a | b | c | d | e | f

Semantics

CharacterValue[HexEscape]: Char16;
CharacterValue[HexEscape  x HexDigit1 HexDigit2] = integerToChar16(16HexValue[HexDigit1] + HexValue[HexDigit2]);
CharacterValue[HexEscape  u HexDigit1 HexDigit2 HexDigit3 HexDigit4] = integerToChar16(4096HexValue[HexDigit1] + 256HexValue[HexDigit2] + 16HexValue[HexDigit3] + HexValue[HexDigit4]);

Character Class Escapes

Syntax

CharacterClassEscape 
   s
|  S
|  d
|  D
|  w
|  W

Semantics

AcceptanceSet[CharacterClassEscape]: Char16{};
AcceptanceSet[CharacterClassEscape  s] = reWhitespaces;
AcceptanceSet[CharacterClassEscape  S] = {‘«NUL»’ ... ‘«uFFFF»’} – reWhitespaces;
AcceptanceSet[CharacterClassEscape  d] = reDigits;
AcceptanceSet[CharacterClassEscape  D] = {‘«NUL»’ ... ‘«uFFFF»’} – reDigits;
AcceptanceSet[CharacterClassEscape  w] = reWordCharacters;
AcceptanceSet[CharacterClassEscape  W] = {‘«NUL»’ ... ‘«uFFFF»’} – reWordCharacters;

User-Specified Character Classes

Syntax

CharacterClass 
   [ [lookahead{^}] ClassRanges ]
|  [ ^ ClassRanges ]
ClassRanges 
   «empty»
  {dashnoDash}
NonemptyClassRanges 
   ClassAtomdash
|  ClassAtom NonemptyClassRangesnoDash
|  ClassAtom - ClassAtomdash ClassRanges

Semantics

AcceptanceSet[CharacterClass]: Char16{};
AcceptanceSet[CharacterClass  [ [lookahead{^}] ClassRanges ]] = AcceptanceSet[ClassRanges];
AcceptanceSet[CharacterClass  [ ^ ClassRanges ]] = AcceptanceSet[ClassRanges];
Invert[CharacterClass  [ [lookahead{^}] ClassRanges ]] = false;
Invert[CharacterClass  [ ^ ClassRanges ]] = true;
AcceptanceSet[ClassRanges]: Char16{};
AcceptanceSet[ClassRanges  «empty»] = {};
AcceptanceSet[ClassRanges  NonemptyClassRangesdash] = AcceptanceSet[NonemptyClassRangesdash];
AcceptanceSet[NonemptyClassRanges]: Char16{};
AcceptanceSet[NonemptyClassRanges  ClassAtomdash] = AcceptanceSet[ClassAtomdash];
AcceptanceSet[NonemptyClassRanges0  ClassAtom NonemptyClassRangesnoDash1] = AcceptanceSet[ClassAtom AcceptanceSet[NonemptyClassRangesnoDash1];
AcceptanceSet[NonemptyClassRanges  ClassAtom1 - ClassAtomdash2 ClassRanges] = characterRange(AcceptanceSet[ClassAtom1], AcceptanceSet[ClassAtomdash2])  AcceptanceSet[ClassRanges];
AcceptanceSet[NonemptyClassRanges  NullEscape ClassRanges] = AcceptanceSet[ClassRanges];
proc characterRange(lowChar16{}, highChar16{}): Char16{}
if |low 1 or |high 1 then throw syntaxError end if;
lChar16  the one element of low;
hChar16  the one element of high;
if l  h then return {l ... helse throw syntaxError end if
end proc;

Character Class Range Atoms

Syntax

ClassAtom 
   ClassCharacter
|  \ ClassEscape
ClassCharacterdash  UnicodeCharacter except \ | ]
ClassCharacternoDash  ClassCharacterdash except -
ClassEscape 
|  b

Semantics

AcceptanceSet[ClassAtom]: Char16{};
AcceptanceSet[ClassAtom  ClassCharacter] = {ClassCharacter};
AcceptanceSet[ClassAtom  \ ClassEscape] = AcceptanceSet[ClassEscape];
AcceptanceSet[ClassEscape]: Char16{};
AcceptanceSet[ClassEscape  DecimalEscape]
begin
if EscapeValue[DecimalEscape] = 0 then return {‘«NUL»’}
else throw syntaxError
end if
end;
AcceptanceSet[ClassEscape  b] = {‘«BS»’};
AcceptanceSet[ClassEscape  CharacterEscape] = {CharacterValue[CharacterEscape]};
AcceptanceSet[ClassEscape  CharacterClassEscape] = AcceptanceSet[CharacterClassEscape];

ECMAScript 4 Netscape Proposal
Formal Description
Syntactic Grammar
previousupnext

Monday, June 30, 2003

This LALR(1) grammar describes the syntax of the ECMAScript 4 proposal. The starting nonterminal is Program. See also the description of the grammar notation.

This document is also available as a Word RTF file.

Terminals

General tokens: Identifier NegatedMinLong Number RegularExpression String VirtualSemicolon

Punctuation tokens: ! != !== % %= & && &&= &= ( ) * *= + ++ += , - -- -= . ... / /= : :: ; < << <<= <= = == === > >= >> >>= >>> >>>= ? [ ] ^ ^= ^^ ^^= { | |= || ||= } ~

Reserved words: as break case catch class const continue default delete do else extends false finally for function if import in instanceof is namespace new null package private public return super switch this throw true try typeof use var void while with

Future reserved words: abstract debugger enum export goto implements interface native protected synchronized throws transient volatile

Non-reserved words: get set

Expressions

  {allowInnoIn}

Identifiers

Identifier 
   Identifier
|  get
|  set

Qualified Identifiers

SimpleQualifiedIdentifier 
ExpressionQualifiedIdentifier  ParenExpression :: Identifier
QualifiedIdentifier 

Primary Expressions

PrimaryExpression 
   null
|  true
|  false
|  Number
|  String
|  this
|  RegularExpression
ReservedNamespace 
   public
|  private
ParenExpression  ( AssignmentExpressionallowIn )
ParenListExpression 
|  ( ListExpressionallowIn , AssignmentExpressionallowIn )

Function Expressions

FunctionExpression 
   function FunctionCommon
|  function Identifier FunctionCommon

Object Literals

ObjectLiteral  { FieldList }
FieldList 
   «empty»
NonemptyFieldList 
LiteralField  FieldName : AssignmentExpressionallowIn
FieldName 
|  String
|  Number

Array Literals

ArrayLiteral  [ ElementList ]
ElementList 
   «empty»
|  , ElementList
LiteralElement  AssignmentExpressionallowIn

Super Expressions

SuperExpression 
   super
|  super ParenExpression

Postfix Expressions

PostfixExpression 
AttributeExpression 
FullPostfixExpression 
|  PostfixExpression [no line break] ++
|  PostfixExpression [no line break] --
FullNewExpression  new FullNewSubexpression Arguments
FullNewSubexpression 
ShortNewExpression  new ShortNewSubexpression
ShortNewSubexpression 

Property Operators

PropertyOperator 
Brackets 
   [ ]
|  [ ListExpressionallowIn ]
Arguments 
   ( )
ExpressionsWithRest 
RestExpression  ... AssignmentExpressionallowIn

Unary Operators

UnaryExpression 
|  delete PostfixExpression
|  void UnaryExpression
|  typeof UnaryExpression
|  - NegatedMinLong

Multiplicative Operators

MultiplicativeExpression 

Additive Operators

AdditiveExpression 

Bitwise Shift Operators

ShiftExpression 

Relational Operators

RelationalExpressionallowIn 
|  RelationalExpressionallowIn instanceof ShiftExpression
RelationalExpressionnoIn 

Equality Operators

EqualityExpression 
   RelationalExpression
|  EqualityExpression == RelationalExpression
|  EqualityExpression != RelationalExpression
|  EqualityExpression === RelationalExpression
|  EqualityExpression !== RelationalExpression

Binary Bitwise Operators

BitwiseAndExpression 
   EqualityExpression
|  BitwiseAndExpression & EqualityExpression
BitwiseXorExpression 
   BitwiseAndExpression
|  BitwiseXorExpression ^ BitwiseAndExpression
BitwiseOrExpression 
   BitwiseXorExpression
|  BitwiseOrExpression | BitwiseXorExpression

Binary Logical Operators

LogicalAndExpression 
   BitwiseOrExpression
|  LogicalAndExpression && BitwiseOrExpression
LogicalXorExpression 
   LogicalAndExpression
|  LogicalXorExpression ^^ LogicalAndExpression
LogicalOrExpression 
   LogicalXorExpression
|  LogicalOrExpression || LogicalXorExpression

Conditional Operator

ConditionalExpression 
   LogicalOrExpression
|  LogicalOrExpression ? AssignmentExpression : AssignmentExpression
NonAssignmentExpression 
   LogicalOrExpression
|  LogicalOrExpression ? NonAssignmentExpression : NonAssignmentExpression

Assignment Operators

AssignmentExpression 
   ConditionalExpression
|  PostfixExpression = AssignmentExpression
CompoundAssignment 
   *=
|  /=
|  %=
|  +=
|  -=
|  <<=
|  >>=
|  >>>=
|  &=
|  ^=
|  |=
LogicalAssignment 
   &&=
|  ^^=
|  ||=

Comma Expressions

ListExpression 
   AssignmentExpression
|  ListExpression , AssignmentExpression

Type Expressions

TypeExpression  NonAssignmentExpression

Statements

  {abbrevnoShortIffull}
Statement 
   ExpressionStatement Semicolon
|  SuperStatement Semicolon
|  Block
|  LabeledStatement
|  IfStatement
|  DoStatement Semicolon
|  WhileStatement
|  ForStatement
|  WithStatement
|  ContinueStatement Semicolon
|  BreakStatement Semicolon
|  ReturnStatement Semicolon
|  ThrowStatement Semicolon
Substatement 
|  Statement
|  SimpleVariableDefinition Semicolon
|  Attributes [no line break] { Substatements }
Substatements 
   «empty»
SubstatementsPrefix 
   «empty»
Semicolonabbrev 
   ;
|  VirtualSemicolon
|  «empty»
SemicolonnoShortIf 
   ;
|  VirtualSemicolon
|  «empty»
Semicolonfull 
   ;
|  VirtualSemicolon

Empty Statement

EmptyStatement  ;

Expression Statement

ExpressionStatement  [lookahead{function{}] ListExpressionallowIn

Super Statement

SuperStatement  super Arguments

Block Statement

Block  { Directives }

Labeled Statements

LabeledStatement  Identifier : Substatement

If Statement

IfStatementabbrev 
|  if ParenListExpression SubstatementnoShortIf else Substatementabbrev
IfStatementfull 
|  if ParenListExpression SubstatementnoShortIf else Substatementfull
IfStatementnoShortIf  if ParenListExpression SubstatementnoShortIf else SubstatementnoShortIf

Switch Statement

SwitchStatement  switch ParenListExpression { CaseElements }
CaseElements 
   «empty»
CaseElementsPrefix 
   «empty»
CaseElement 
   Directive
CaseLabel 
   case ListExpressionallowIn :
|  default :

Do-While Statement

DoStatement  do Substatementabbrev while ParenListExpression

While Statement

WhileStatement  while ParenListExpression Substatement

For Statements

ForStatement 
   for ( ForInitializer ; OptionalExpression ; OptionalExpression ) Substatement
|  for ( ForInBinding in ListExpressionallowIn ) Substatement
ForInitializer 
   «empty»
|  Attributes [no line break] VariableDefinitionnoIn
ForInBinding 
|  Attributes [no line break] VariableDefinitionKind VariableBindingnoIn
OptionalExpression 
   ListExpressionallowIn
|  «empty»

With Statement

WithStatement  with ParenListExpression Substatement

Continue and Break Statements

ContinueStatement 
   continue
|  continue [no line break] Identifier
BreakStatement 
   break
|  break [no line break] Identifier

Return Statement

ReturnStatement 
   return
|  return [no line break] ListExpressionallowIn

Throw Statement

ThrowStatement  throw [no line break] ListExpressionallowIn

Try Statement

TryStatement 
   try Block CatchClauses
|  try Block CatchClausesOpt finally Block
CatchClausesOpt 
   «empty»
CatchClauses 
CatchClause  catch ( Parameter ) Block

Directives

Directive 
|  Statement
|  AnnotatableDirective
|  Attributes [no line break] AnnotatableDirective
|  Attributes [no line break] { Directives }
|  Pragma Semicolon
AnnotatableDirective 
   VariableDefinitionallowIn Semicolon
|  NamespaceDefinition Semicolon
|  ImportDirective Semicolon
|  UseDirective Semicolon
Directives 
   «empty»
DirectivesPrefix 
   «empty»

Attributes

Attributes 
   Attribute
AttributeCombination  Attribute [no line break] Attributes
Attribute 
|  true
|  false

Use Directive

UseDirective  use namespace ParenListExpression

Import Directive

ImportDirective 
   import PackageName
|  import Identifier = PackageName

Pragma

Pragma  use PragmaItems
PragmaItems 
PragmaItem 
|  PragmaExpr ?
PragmaExpr 
PragmaArgument 
   true
|  false
|  Number
|  - Number
|  - NegatedMinLong
|  String

Definitions

Variable Definition

VariableDefinition  VariableDefinitionKind VariableBindingList
VariableDefinitionKind 
   var
|  const
VariableBindingList 
   VariableBinding
|  VariableBindingList , VariableBinding
VariableBinding  TypedIdentifier VariableInitialisation
VariableInitialisation 
   «empty»
|  = VariableInitializer
VariableInitializer 
   AssignmentExpression
TypedIdentifier 
|  Identifier : TypeExpression

Simple Variable Definition

A SimpleVariableDefinition represents the subset of VariableDefinition expansions that may be used when the variable definition is used as a Substatement instead of a Directive in non-strict mode. In strict mode variable definitions may not be used as substatements.

SimpleVariableDefinition  var UntypedVariableBindingList
UntypedVariableBindingList 
UntypedVariableBinding  Identifier VariableInitialisationallowIn

Function Definition

FunctionDefinition  function FunctionName FunctionCommon
FunctionName 
|  get [no line break] Identifier
|  set [no line break] Identifier
FunctionCommon  ( Parameters ) Result Block
Parameters 
   «empty»
NonemptyParameters 
Parameter  ParameterAttributes TypedIdentifierallowIn
ParameterAttributes 
   «empty»
|  const
ParameterInit 
   Parameter
RestParameter 
   ...
Result 
   «empty»
|  : TypeExpressionallowIn

Class Definition

ClassDefinition  class Identifier Inheritance Block
Inheritance 
   «empty»
|  extends TypeExpressionallowIn

Namespace Definition

NamespaceDefinition  namespace Identifier

Programs

Program 

Package Definition

PackageDefinition  package PackageNameOpt Block
PackageNameOpt 
   «empty»
PackageName 
   String
PackageIdentifiers 

ECMAScript 4 Netscape Proposal
Formal Description
Syntactic Semantics
previousupnext

Monday, June 30, 2003

The syntactic semantics describe the actions the parser takes to evaluate an ECMAScript 4 program. For convenience, the syntactic grammar is repeated here. The starting nonterminal is Program. See also the description of the semantic notation.

The semantics are under construction. Many execution paths resulting in ???? represent semantics yet to be implemented.

This document is also available as a Word RTF file.

Terminals

General tokens: Identifier NegatedMinLong Number RegularExpression String VirtualSemicolon

Punctuation tokens: ! != !== % %= & && &&= &= ( ) * *= + ++ += , - -- -= . ... / /= : :: ; < << <<= <= = == === > >= >> >>= >>> >>>= ? [ ] ^ ^= ^^ ^^= { | |= || ||= } ~

Reserved words: as break case catch class const continue default delete do else extends false finally for function if import in instanceof is namespace new null package private public return super switch this throw true try typeof use var void while with

Future reserved words: abstract debugger enum export goto implements interface native protected synchronized throws transient volatile

Non-reserved words: get set

Data Model

Semantic Exceptions

tuple Break
valueObject,
labelLabel
end tuple;
tuple Continue
valueObject,
labelLabel
end tuple;
tuple Return
valueObject
end tuple;
ControlTransfer = Break  Continue  Return;
SemanticException = Object  ControlTransfer;

Extended integers and rationals

tag +zero;
tag –zero;
tag +;
tag ;
tag NaN;
ExtendedRational = (Rational – {0})  {+zero–zero+NaN};
ExtendedInteger = Integer  {+NaN};

Objects

tag none;
tag ok;
tag reject;
ObjectUndefined  Null  Boolean  Long  ULong  Float32  Float64  Char16  String  Namespace  CompoundAttribute  Class  SimpleInstance  MethodClosure  Date  RegExp  Package;
PrimitiveObjectUndefined  Null  Boolean  Long  ULong  Float32  Float64  Char16  String;
NonprimitiveObjectNamespace  CompoundAttribute  Class  SimpleInstance  MethodClosure  Date  RegExp  Package;
BindingObject = Class  SimpleInstance  RegExp  Date  Package;
ObjectOpt = Object  {none};
BooleanOpt = Boolean  {none};
IntegerOpt = Integer  {none};

Undefined

tag undefined;
Undefined = {undefined};

Null

tag null;
Null = {null};

Strings

StringOpt = String  {none};

Namespaces

record Namespace
nameString
end record;

Qualified Names

tuple QualifiedName
namespaceNamespace,
idString
end tuple;
The notation ns::id is a shorthand for QualifiedNamenamespacensidid.
Multiname = QualifiedName{};

Attributes

tag static;
tag virtual;
tag final;
PropertyCategory = {nonestaticvirtualfinal};
OverrideModifier = {nonetruefalseundefined};
tuple CompoundAttribute
namespacesNamespace{},
explicitBoolean,
enumerableBoolean,
dynamicBoolean,
categoryPropertyCategory,
overrideModOverrideModifier,
prototypeBoolean,
unusedBoolean
end tuple;
Attribute = Boolean  Namespace  CompoundAttribute;
AttributeOptNotFalse = {nonetrue Namespace  CompoundAttribute;

Classes

record Class
localBindingsLocalBinding{},
instancePropertiesInstanceProperty{},
superClassOpt,
prototypeObjectOpt,
completeBoolean,
nameString,
typeofStringString,
privateNamespaceNamespace,
dynamicBoolean,
finalBoolean,
defaultValueObjectOpt,
defaultHintHint,
hasPropertyObject  Class  Object  Boolean  Phase  Boolean,
bracketReadObject  Class  Object[]  Boolean  Phase  ObjectOpt,
bracketWriteObject  Class  Object[]  Object  Boolean  {run {noneok},
bracketDeleteObject  Class  Object[]  {run BooleanOpt,
readObject  Class  Multiname  EnvironmentOpt  Boolean  Phase  ObjectOpt,
writeObject  Class  Multiname  EnvironmentOpt  Object  Boolean  {run {none, ok},
deleteObject  Class  Multiname  EnvironmentOpt  {run BooleanOpt,
enumerateObject  Object{},
callObject  Class  Object[]  Phase  Object,
constructClass  Object[]  Phase  Object,
init: (SimpleInstance  Object[]  {run ())  {none},
isObject  Class  Boolean,
coerceObject  Class  ObjectOpt
end record;
ClassOpt = Class  {none};

Simple Instances

record SimpleInstance
localBindingsLocalBinding{},
archetypeObjectOpt,
sealedBoolean,
typeClass,
slotsSlot{},
call: (Object  SimpleInstance  Object[]  Phase  Object {none},
construct: (SimpleInstance  Object[]  Phase  Object {none},
end record;

Slots

record Slot
valueObjectOpt
end record;

Uninstantiated Functions

record UninstantiatedFunction
typeClass,
lengthInteger,
call: (Object  SimpleInstance  Object[]  Phase  Object {none},
construct: (SimpleInstance  Object[]  Phase  Object {none},
instantiationsSimpleInstance{}
end record;

Method Closures

tuple MethodClosure
thisObject,
methodInstanceMethod,
slotsSlot{}
end tuple;

Dates

record Date
localBindingsLocalBinding{},
archetypeObjectOpt,
sealedBoolean,
timeValueInteger
end record;

Regular Expressions

record RegExp
localBindingsLocalBinding{},
archetypeObjectOpt,
sealedBoolean,
sourceString,
lastIndexInteger,
globalBoolean,
ignoreCaseBoolean,
multilineBoolean
end record;

Packages

record Package
localBindingsLocalBinding{},
archetypeObjectOpt,
nameString,
initialize: (()  ())  {nonebusy},
sealedBoolean,
internalNamespaceNamespace
end record;

Objects with Limits

instance must be an instance of one of limit’s descendants.
tuple LimitedInstance
instanceObject,
limitClass
end tuple;
ObjOptionalLimit = Object  LimitedInstance;

References

tuple LexicalReference
variableMultinameMultiname,
strictBoolean
end tuple;
tuple DotReference
baseObject,
limitClass,
multinameMultiname
end tuple;
tuple BracketReference
baseObject,
limitClass,
argsObject[]
end tuple;
Reference = LexicalReference  DotReference  BracketReference;
ObjOrRef = Object  Reference;

Modes of expression evaluation

tag compile;
tag run;
Phase = {compilerun};

Contexts

record Context
strictBoolean,
openNamespacesNamespace{}
end record;

Labels

tag default;
Label = String  {default};
tuple JumpTargets
breakTargetsLabel{},
continueTargetsLabel{}
end tuple;

Function Support

tag normal;
tag get;
tag set;
Handling = {normalgetset};
tag plainFunction;
tag uncheckedFunction;
tag prototypeFunction;
tag instanceFunction;
tag constructorFunction;
StaticFunctionKind = {plainFunctionuncheckedFunctionprototypeFunction};

Environments

An Environment is a list of two or more frames. Each frame corresponds to a scope. More specific frames are listed first—each frame’s scope is directly contained in the following frame’s scope. The last frame is always a Package. A WithFrame is always preceded by a LocalFrame, so the first frame is never a WithFrame.
Environment = Frame[];
EnvironmentOpt = Environment  {none};
Frame = NonWithFrame  WithFrame;
NonWithFrame = Package  ParameterFrame  Class  LocalFrame;
record ParameterFrame
localBindingsLocalBinding{},
kindFunctionKind,
handlingHandling,
callsSuperconstructorBoolean,
superconstructorCalledBoolean,
thisObjectOpt,
parametersParameter[],
restVariableOpt,
returnTypeClass
end record;
ParameterFrameOpt = ParameterFrame  {none};
tuple Parameter
varVariable  DynamicVar,
defaultObjectOpt
end tuple;
record LocalFrame
localBindingsLocalBinding{}
end record;
record WithFrame
valueObjectOpt
end record;

Properties

tag read;
tag write;
tag readWrite;
Access = {readwrite};
AccessSet = {readwritereadWrite};
tuple LocalBinding
qnameQualifiedName,
accessesAccessSet,
explicitBoolean,
enumerableBoolean,
end tuple;
tag forbidden;
SingletonProperty = {forbidden Variable  DynamicVar  Getter  Setter;
SingletonPropertyOpt = SingletonProperty  {none};
VariableValue = {none Object  UninstantiatedFunction;
tag busy;
Initializer = Environment  Phase  Object;
InitializerOpt = Initializer  {none};
record Variable
typeClass,
valueVariableValue,
immutableBoolean,
setup: (()  ClassOpt {nonebusy},
initializerInitializer  {nonebusy},
initializerEnvEnvironment
end record;
VariableOpt = Variable  {none};
record DynamicVar
valueObject  UninstantiatedFunction,
sealedBoolean
end record;
record Getter
callEnvironment  Phase  Object,
end record;
record Setter
callObject  Environment  Phase  (),
end record;
InstanceProperty = InstanceVariable  InstanceMethod  InstanceGetter  InstanceSetter;
InstancePropertyOpt = InstanceProperty  {none};
record InstanceVariable
multinameMultiname,
finalBoolean,
enumerableBoolean,
typeClass,
defaultValueObjectOpt,
immutableBoolean
end record;
InstanceVariableOpt = InstanceVariable  {none};
record InstanceMethod
multinameMultiname,
finalBoolean,
enumerableBoolean,
signatureParameterFrame,
lengthInteger,
callObject  Object[]  Phase  Object
end record;
record InstanceGetter
multinameMultiname,
finalBoolean,
enumerableBoolean,
signatureParameterFrame,
callObject  Phase  Object
end record;
record InstanceSetter
multinameMultiname,
finalBoolean,
enumerableBoolean,
signatureParameterFrame,
callObject  Object  Phase  ()
end record;
PropertyOpt = SingletonProperty  InstanceProperty  {none};

Miscellaneous

tag hintString;
tag hintNumber;
Hint = {hintStringhintNumber};
HintOpt = Hint  {none};
tag less;
tag equal;
tag greater;
tag unordered;
Order = {lessequalgreaterunordered};

Data Operations

Numeric Utilities

unsignedWrap32(i) returns i converted to a value between 0 and 232–1 inclusive, wrapping around modulo 232 if necessary.
proc unsignedWrap32(iInteger): {0 ... 232 – 1}
return bitwiseAnd(i, 0xFFFFFFFF)
end proc;
signedWrap32(i) returns i converted to a value between –231 and 231–1 inclusive, wrapping around modulo 232 if necessary.
proc signedWrap32(iInteger): {–231 ... 231 – 1}
jInteger  bitwiseAnd(i, 0xFFFFFFFF);
if j  231 then j  j – 232 end if;
return j
end proc;
unsignedWrap64(i) returns i converted to a value between 0 and 264–1 inclusive, wrapping around modulo 264 if necessary.
proc unsignedWrap64(iInteger): {0 ... 264 – 1}
return bitwiseAnd(i, 0xFFFFFFFFFFFFFFFF)
end proc;
signedWrap64(i) returns i converted to a value between –263 and 263–1 inclusive, wrapping around modulo 264 if necessary.
proc signedWrap64(iInteger): {–263 ... 263 – 1}
jInteger  bitwiseAnd(i, 0xFFFFFFFFFFFFFFFF);
if j  263 then j  j – 264 end if;
return j
end proc;
truncateToInteger(x) returns x converted to an integer by rounding towards zero. If x is an infinity or a NaN, the result is 0.
proc truncateToInteger(xGeneralNumber): Integer
case x of
{NaNf32NaNf64+f32+f64f32f64do return 0;
Long  ULong do return x.value
end case
end proc;
pinExtendedInteger(ilimitnegativeFromEnd) returns i pinned to the set {0 ... limit}, where limit is a nonnegative integer. If negativeFromEnd is true, then negative values of i from –limit through –1 are treated as 0 through limit – 1 respectively.
proc pinExtendedInteger(iExtendedIntegerlimitIntegernegativeFromEndBoolean): Integer
case i of
{NaNdo throw a RangeError exception;
{do return 0;
{+do return limit;
jInteger  i;
if j > limit then j  limit end if;
if negativeFromEnd and j < 0 then j  j + limit end if;
if j < 0 then j  0 end if;
note  0  j  limit;
return j
end case
end proc;
checkInteger(x) returns x converted to an integer if its mathematical value is, in fact, an integer. If x is an infinity or a NaN or has a fractional part, the result is none.
proc checkInteger(xGeneralNumber): IntegerOpt
case x of
{NaNf32NaNf64+f32+f64f32f64do return none;
{+zerof32+zerof64–zerof32–zerof64do return 0;
Long  ULong do return x.value;
NonzeroFiniteFloat32  NonzeroFiniteFloat64 do
rRational  x.value;
if r  Integer then return none end if;
return r
end case
end proc;
integerToLong(i) converts i to the first of the types Long, ULong, or Float64 that can contain the value i. If necessary, the Float64 result may be rounded or converted to an infinity using the IEEE 754 “round to nearest” mode.
proc integerToLong(iInteger): GeneralNumber
if –263  i  263 – 1 then return ilong
elsif 263  i  264 – 1 then return iulong
else return if64
end if
end proc;
integerToULong(i) converts i to the first of the types ULong, Long, or Float64 that can contain the value i. If necessary, the Float64 result may be rounded or converted to an infinity using the IEEE 754 “round to nearest” mode.
proc integerToULong(iInteger): GeneralNumber
if 0  i  264 – 1 then return iulong
elsif –263  i  –1 then return ilong
else return if64
end if
end proc;
rationalToLong(q) converts q to one of the types Long, ULong, or Float64, whichever one can come the closest to representing the true value of q. If several of these types can come equally close to the value of q, then one of them is chosen according to the algorithm below.
proc rationalToLong(qRational): GeneralNumber
if q  Integer then return integerToLong(q)
elsif |q 253 then return qf64
elsif q < –263 – 1/2 or q  264 – 1/2 then return qf64
else
Let i be the integer closest to q. If q is halfway between two integers, pick i so that it is even.
note  –263  i  264 – 1;
if i < 263 then return ilong else return iulong end if
end if
end proc;
rationalToULong(q) converts q to one of the types ULong, Long, or Float64, whichever one can come the closest to representing the true value of q. If several of these types can come equally close to the value of q, then one of them is chosen according to the algorithm below.
proc rationalToULong(qRational): GeneralNumber
if q  Integer then return integerToULong(q)
elsif |q 253 then return qf64
elsif q < –263 – 1/2 or q  264 – 1/2 then return qf64
else
Let i be the integer closest to q. If q is halfway between two integers, pick i so that it is even.
note  –263  i  264 – 1;
if i  0 then return iulong else return ilong end if
end if
end proc;
proc extendedRationalToFloat32(qExtendedRational): Float32
case q of
Rational do return qf32;
{+zerodo return +zerof32;
{–zerodo return –zerof32;
{+do return +f32;
{do return f32;
{NaNdo return NaNf32
end case
end proc;
proc extendedRationalToFloat64(qExtendedRational): Float64
case q of
Rational do return qf64;
{+zerodo return +zerof64;
{–zerodo return –zerof64;
{+do return +f64;
{do return f64;
{NaNdo return NaNf64
end case
end proc;
toRational(x) returns the exact Rational value of x.
proc toRational(xFiniteGeneralNumber): Rational
case x of
{+zerof32+zerof64–zerof32–zerof64do return 0;
NonzeroFiniteFloat32  NonzeroFiniteFloat64  Long  ULong do return x.value
end case
end proc;
toFloat32(x) converts x to a Float32, using the IEEE 754 “round to nearest” mode.
proc toFloat32(xGeneralNumber): Float32
case x of
Long  ULong do return (x.value)f32;
Float32 do return x;
{f64do return f32;
{–zerof64do return –zerof32;
{+zerof64do return +zerof32;
{+f64do return +f32;
{NaNf64do return NaNf32;
NonzeroFiniteFloat64 do return (x.value)f32
end case
end proc;
toFloat64(x) converts x to a Float64, using the IEEE 754 “round to nearest” mode.
proc toFloat64(xGeneralNumber): Float64
case x of
Long  ULong do return (x.value)f64;
Float32 do return float32ToFloat64(x);
Float64 do return x
end case
end proc;
generalNumberCompare(xy) compares x with y using the IEEE 754 rules and returns less if x<y, equal if x=y, greater if x>y, or unordered if either x or y is a NaN. The comparison is done using the exact values of x and y, even if they have different types. Positive infinities compare equal to each other and greater than any other non-NaN values. Negative infinities compare equal to each other and less than any other non-NaN values. Positive and negative zeroes compare equal to each other.
proc generalNumberCompare(xGeneralNumberyGeneralNumber): Order
if x  {NaNf32NaNf64or y  {NaNf32NaNf64then return unordered
elsif x  {+f32+f64and y  {+f32+f64then return equal
elsif x  {f32f64and y  {f32f64then return equal
elsif x  {+f32+f64or y  {f32f64then return greater
elsif x  {f32f64or y  {+f32+f64then return less
else
xrRational  toRational(x);
yrRational  toRational(y);
if xr < yr then return less
elsif xr > yr then return greater
else return equal
end if
end if
end proc;

Character Utilities

proc integerToUTF16(i: {0 ... 0x10FFFF}): String
if 0  i  0xFFFF then return [integerToChar16(i)]
else
j: {0 ... 0xFFFFF}  i – 0x10000;
highChar16  integerToChar16(0xD800 + bitwiseShift(j, –10));
lowChar16  integerToChar16(0xDC00 + bitwiseAnd(j, 0x3FF));
return [highlow]
end if
end proc;
proc char21ToUTF16(chChar21): String
end proc;
proc surrogatePairToSupplementaryChar(hChar16lChar16): SupplementaryChar
codePoint: {0x10000 ... 0x10FFFF}  0x10000 + (char16ToInteger(h) – 0xD800)0x400 + char16ToInteger(l) – 0xDC00;
return integerToSupplementaryChar(codePoint)
end proc;
proc stringToUTF32(sString): Char21[]
iInteger  0;
resultChar21[]  [];
while i  |sdo
chChar21;
if s[i {‘«uD800»’ ... ‘«uDBFF»’} and i + 1  |sand s[i + 1]  {‘«uDC00»’ ... ‘«uDFFF»’} then
ch  surrogatePairToSupplementaryChar(s[i], s[i + 1]);
i  i + 2
else ch  s[i]; i  i + 1
end if;
result  result  [ch]
end while;
return result
end proc;
proc charToLowerFull(chChar21): String
return ch converted to a lower case character using the Unicode full, locale-independent case mapping. A single character may be converted to multiple characters. If ch has no lower case equivalent, then the result is the string char21ToUTF16(ch).
end proc;
proc charToLowerLocalized(chChar21): String
return ch converted to a lower case character using the Unicode full case mapping in the host environment’s current locale. A single character may be converted to multiple characters. If ch has no lower case equivalent, then the result is the string char21ToUTF16(ch).
end proc;
proc charToUpperFull(chChar21): String
return ch converted to a upper case character using the Unicode full, locale-independent case mapping. A single character may be converted to multiple characters. If ch has no upper case equivalent, then the result is the string char21ToUTF16(ch).
end proc;
proc charToUpperLocalized(chChar21): String
return ch converted to a upper case character using the Unicode full case mapping in the host environment’s current locale. A single character may be converted to multiple characters. If ch has no upper case equivalent, then the result is the string char21ToUTF16(ch).
end proc;

Object Utilities

Object Class Inquiries

objectType(o) returns an Object o’s most specific type. Although objectType is used internally throughout this specification, in order to allow one programmer-visible class to be implemented as an ensemble of implementation-specific classes, no way is provided for a user program to directly obtain the result of calling objectType on an object.
proc objectType(oObject): Class
case o of
Undefined do return Void;
Null do return Null;
Boolean do return Boolean;
Long do return long;
ULong do return ulong;
Float32 do return float;
Float64 do return Number;
Char16 do return char;
String do return String;
Namespace do return Namespace;
Class do return Class;
SimpleInstance do return o.type;
MethodClosure do return Function;
Date do return Date;
RegExp do return RegExp;
Package do return Package
end case
end proc;
is(oc) returns true if o is an instance of class c or one of its subclasses.
proc is(oObjectcClass): Boolean
return c.is(oc)
end proc;
ordinaryIs(oc) is the implementation of is for a native class unless specified otherwise in the class’s definition. Host classes may either also use ordinaryIs or define a different procedure to perform this test.
proc ordinaryIs(oObjectcClass): Boolean
return isAncestor(cobjectType(o))
end proc;
Return an ordered list of class c’s ancestors, including c itself.
proc ancestors(cClass): Class[]
sClassOpt  c.super;
if s = none then return [c] else return ancestors(s [c] end if
end proc;
Return true if c is d or an ancestor of d.
proc isAncestor(cClassdClass): Boolean
if c = d then return true
else
sClassOpt  d.super;
if s = none then return false end if;
return isAncestor(cs)
end if
end proc;

Object to Boolean Conversion

objectToBoolean(o) returns o converted to a Boolean.
proc objectToBoolean(oObject): Boolean
case o of
Undefined  Null do return false;
Boolean do return o;
Long  ULong do return o.value  0;
Float32 do return o  {+zerof32–zerof32NaNf32};
Float64 do return o  {+zerof64–zerof64NaNf64};
String do return o  “”;
Char16  Namespace  CompoundAttribute  Class  SimpleInstance  MethodClosure  Date  RegExp  Package do
return true
end case
end proc;

Object to Primitive Conversion

proc objectToPrimitive(oObjecthintHintOptphasePhase): PrimitiveObject
if o  PrimitiveObject then return o end if;
cClass  objectType(o);
hHint;
if hint  Hint then h  hint else h  c.defaultHint end if;
case h of
toStringMethodObjectOpt  c.read(oc, {public::“toString”}, nonefalsephase);
if toStringMethod  none then
rObject  call(otoStringMethod[]phase);
if r  PrimitiveObject then return r end if
end if;
valueOfMethodObjectOpt  c.read(oc, {public::“valueOf”}, nonefalsephase);
if valueOfMethod  none then
rObject  call(ovalueOfMethod[]phase);
if r  PrimitiveObject then return r end if
end if;
valueOfMethodObjectOpt  c.read(oc, {public::“valueOf”}, nonefalsephase);
if valueOfMethod  none then
rObject  call(ovalueOfMethod[]phase);
if r  PrimitiveObject then return r end if
end if;
toStringMethodObjectOpt  c.read(oc, {public::“toString”}, nonefalsephase);
if toStringMethod  none then
rObject  call(otoStringMethod[]phase);
if r  PrimitiveObject then return r end if
end if
end case;
throw a TypeError exception — cannot convert this object to a primitive
end proc;

Object to Number Conversions

objectToGeneralNumber(ophase) returns o converted to a GeneralNumber. If phase is compile, only constant conversions are permitted.
proc objectToGeneralNumber(oObjectphasePhase): GeneralNumber
aPrimitiveObject  objectToPrimitive(ohintNumberphase);
case a of
Undefined do return NaNf64;
Null  {falsedo return +zerof64;
{truedo return 1f64;
GeneralNumber do return a;
Char16  String do return stringToFloat64(toString(a))
end case
end proc;
objectToFloat32(ophase) returns o converted to a Float32. If phase is compile, only constant conversions are permitted.
proc objectToFloat32(oObjectphasePhase): Float32
aPrimitiveObject  objectToPrimitive(ohintNumberphase);
case a of
Undefined do return NaNf32;
Null  {falsedo return +zerof32;
{truedo return 1f32;
GeneralNumber do return toFloat32(a);
Char16  String do return stringToFloat32(toString(a))
end case
end proc;
objectToFloat64(ophase) returns o converted to a Float64. If phase is compile, only constant conversions are permitted.
proc objectToFloat64(oObjectphasePhase): Float64
return toFloat64(objectToGeneralNumber(ophase))
end proc;
objectToExtendedInteger(ophase) returns o converted to an ExtendedInteger. An error occurs if o has a fractional part or is a NaN. If o is a string, then it is converted exactly. If phase is compile, only constant conversions are permitted.
proc objectToExtendedInteger(oObjectphasePhase): ExtendedInteger
aPrimitiveObject  objectToPrimitive(ohintNumberphase);
case a of
Null  {falsedo return 0;
{truedo return 1;
{undefinedNaNf32NaNf64do return NaN;
{+f32+f64do return +;
{f32f64do return ;
{+zerof32+zerof64–zerof32–zerof64do return 0;
Long  ULong do return a.value;
NonzeroFiniteFloat32  NonzeroFiniteFloat64 do
rRational  a.value;
if r  Integer then
throw a RangeError exception — the value a is not an integer
end if;
return r;
Char16  String do return stringToExtendedInteger(toString(a))
end case
end proc;
objectToInteger(ophase) returns o converted to an Integer. An error occurs if o has a fractional part or is not finite. If o is a string, then it is converted exactly. If phase is compile, only constant conversions are permitted.
proc objectToInteger(oObjectphasePhase): Integer
iExtendedInteger  objectToExtendedInteger(ophase);
case i of
{+NaNdo throw a RangeError exception — i is not an integer;
Integer do return i
end case
end proc;
proc stringToFloat32(sString): Float32
Apply the lexer grammar with the start symbol StringNumericLiteral to the string s.
if the grammar cannot interpret the entire string as an expansion of StringNumericLiteral then
return NaNf32
else
qExtendedRational the value of the action Lex applied to the obtained expansion of the nonterminal StringNumericLiteral;
end if
end proc;
proc stringToFloat64(sString): Float64
Apply the lexer grammar with the start symbol StringNumericLiteral to the string s.
if the grammar cannot interpret the entire string as an expansion of StringNumericLiteral then
return NaNf64
else
qExtendedRational the value of the action Lex applied to the obtained expansion of the nonterminal StringNumericLiteral;
end if
end proc;
proc stringToExtendedInteger(sString): ExtendedInteger
Apply the lexer grammar with the start symbol StringNumericLiteral to the string s.
if the grammar cannot interpret the entire string as an expansion of StringNumericLiteral then
throw a TypeError exception — the string s does not contain a number
else
qExtendedRational the value of the action Lex applied to the obtained expansion of the nonterminal StringNumericLiteral;
case q of
{+zero–zerodo return 0;
if q  Integer then return q
else throw a RangeError exception — the value should be an integer
end if
end case
end if
end proc;

Object to String Conversions

objectToString(ophase) returns o converted to a String. If phase is compile, only constant conversions are permitted.
proc objectToString(oObjectphasePhase): String
aPrimitiveObject  objectToPrimitive(ohintStringphase);
case a of
Undefined do return “undefined”;
Null do return “null”;
{falsedo return “false”;
{truedo return “true”;
Char16 do return [a];
String do return a
end case
end proc;
proc toString(oChar16  String): String
case o of
Char16 do return [o];
String do return o
end case
end proc;
proc generalNumberToString(xGeneralNumber): String
case x of
Long  ULong do return integerToString(x.value);
Float32 do return float32ToString(x);
Float64 do return float64ToString(x)
end case
end proc;
integerToString(i) converts an integer i to a string of one or more decimal digits. If i is negative, the string is preceded by a minus sign.
proc integerToString(iInteger): String
if i < 0 then return [-]  integerToString(–iend if;
qInteger  i/10;
rInteger  i – q10;
cChar16  integerToChar16(r + char16ToInteger(‘0’));
if q = 0 then return [c] else return integerToString(q [c] end if
end proc;
integerToStringWithSign(i) is the same as integerToString(i) except that the resulting string always begins with a plus or minus sign.
proc integerToStringWithSign(iInteger): String
if i  0 then return [+]  integerToString(i)
else return [-]  integerToString(–i)
end if
end proc;
proc exponentialNotationString(digitsStringeInteger): String
mantissaString;
if |digits| = 1 then mantissa  digits
else mantissa  [digits[0]]  “.”  digits[1 ...]
end if;
return mantissa  “e”  integerToStringWithSign(e)
end proc;
float32ToString(x) converts a Float32 x to a string using fixed-point notation if the absolute value of x is between 10–6 inclusive and 1021 exclusive, and exponential notation otherwise. The result has the fewest significant digits possible while still ensuring that converting the string back into a Float32 value would result in the same value x (except that –zerof32 would become +zerof32).
proc float32ToString(xFloat32): String
case x of
{NaNf32do return “NaN”;
{+zerof32–zerof32do return “0”;
{+f32do return “Infinity”;
{f32do return “-Infinity”;
rRational  x.value;
if r < 0 then return “-”  float32ToString(float32Negate(x))
else
Let e, k, and s be integers such that k 1, 10k–1 s 10k, (s10e+1–k)f32 = x, and k is as small as possible.
note  k is the number of digits in the decimal representation of s, s is not divisible by 10, and the least significant digit of s is not necessarily uniquely determined by the above criteria.
When there are multiple possibilities for s according to the rules above, implementations are encouraged but not required to select the one according to the following rule: Select the value of s for which s10e+1–k is closest in value to r; if there are two such possible values of s, choose the one that is even.
digitsString  integerToString(s)
if k – 1  e  20 then return digits  repeat(‘0’, e + 1 – k)
elsif 0  e  20 then return digits[0 ... e “.”  digits[e + 1 ...]
elsif –6  e < 0 then return “0.”  repeat(‘0’, –(e + 1))  digits
else return exponentialNotationString(digitse)
end if
end if
end case
end proc;
float64ToString(x) converts a Float64 x to a string using fixed-point notation if the absolute value of x is between 10–6 inclusive and 1021 exclusive, and exponential notation otherwise. The result has the fewest significant digits possible while still ensuring that converting the string back into a Float64 value would result in the same value x (except that –zerof64 would become +zerof64).
proc float64ToString(xFloat64): String
case x of
{NaNf64do return “NaN”;
{+zerof64–zerof64do return “0”;
{+f64do return “Infinity”;
{f64do return “-Infinity”;
rRational  x.value;
if r < 0 then return “-”  float64ToString(float64Negate(x))
else
Let e, k, and s be integers such that k 1, 10k–1 s 10k, (s10e+1–k)f64 = x, and k is as small as possible.
note  k is the number of digits in the decimal representation of s, s is not divisible by 10, and the least significant digit of s is not necessarily uniquely determined by the above criteria.
When there are multiple possibilities for s according to the rules above, implementations are encouraged but not required to select the one according to the following rule: Select the value of s for which s10e+1–k is closest in value to r; if there are two such possible values of s, choose the one that is even.
digitsString  integerToString(s)
if k – 1  e  20 then return digits  repeat(‘0’, e + 1 – k)
elsif 0  e  20 then return digits[0 ... e “.”  digits[e + 1 ...]
elsif –6  e < 0 then return “0.”  repeat(‘0’, –(e + 1))  digits
else return exponentialNotationString(digitse)
end if
end if
end case
end proc;

Object to Qualified Name Conversion

objectToQualifiedName(ophase) coerces an object o to a qualified name. If phase is compile, only constant conversions are permitted.
proc objectToQualifiedName(oObjectphasePhase): QualifiedName
return public::(objectToString(ophase))
end proc;

Object to Class Conversion

objectToClass(o) returns o converted to a non-null Class.
proc objectToClass(oObject): Class
if o  Class then return o else throw a TypeError exception end if
end proc;

Object to Attribute Conversion

objectToAttribute(o) returns o converted to an attribute.
proc objectToAttribute(oObjectphasePhase): Attribute
if o  Attribute then return o
else
note  If o is not an attribute, try to call it with no arguments.
aObject  call(nullo[]phase);
if a  Attribute then return a else throw a TypeError exception end if
end if
end proc;

Implicit Coercions

coerce(oc) attempts to implicitly coerce o to class c. If the coercion succeeds, coerce returns the coerced value. If not, coerce throws a TypeError.
The coercion always succeeds and returns o unchanged if o is already a member of class c. The value returned from coerce always is a member of class c.
proc coerce(oObjectcClass): Object
resultObjectOpt  c.coerce(oc);
if result  none then return result
else throw a TypeError exception — coercion failed
end if
end proc;
coerceOrNull(oc) attempts to implicitly coerce o to class c. If the coercion succeeds, coerceOrNull returns the coerced value. If not, then coerceOrNull returns null if null is a member of type c; otherwise, coerceOrNull throws a TypeError.
The coercion always succeeds and returns o unchanged if o is already a member of class c. The value returned from coerceOrNull always is a member of class c.
proc coerceOrNull(oObjectcClass): Object
resultObjectOpt  c.coerce(oc);
if result  none then return result
elsif c.coerce(nullc) = null then return null
else throw a TypeError exception — coercion failed
end if
end proc;
coerceNonNull(oc) attempts to implicitly coerce o to class c. If the coercion succeeds and the result is not null, then coerceNonNull returns the coerced value. If not, coerceNonNull throws a TypeError.
proc coerceNonNull(oObjectcClass): Object
resultObjectOpt  c.coerce(oc);
if result  {nonenullthen return result
else throw a TypeError exception — coercion failed
end if
end proc;
ordinaryCoerce(oc) is the implementation of coercion for a native class unless specified otherwise in the class’s definition. Host classes may define a different procedure to perform this coercion.
proc ordinaryCoerce(oObjectcClass): ObjectOpt
if o = null or is(octhen return o else return none end if
end proc;

Attributes

combineAttributes(ab) returns the attribute that results from concatenating the attributes a and b.
proc combineAttributes(aAttributeOptNotFalsebAttribute): Attribute
if b = false then return false
elsif a  {nonetruethen return b
elsif b = true then return a
elsif a  Namespace then
if a = b then return a
elsif b  Namespace then
return CompoundAttributenamespaces: {ab}, explicitfalse, enumerablefalse, dynamicfalse, categorynone, overrideModnone, prototypefalse, unusedfalse
else return CompoundAttributenamespacesb.namespaces  {a}, other fields from b
end if
elsif b  Namespace then
return CompoundAttributenamespacesa.namespaces  {b}, other fields from a
else
note  At this point both a and b are compound attributes.
if (a.category  none and b.category  none and a.category  b.categoryor (a.overrideMod  none and b.overrideMod  none and a.overrideMod  b.overrideModthen
throw an AttributeError exception — attributes a and b have conflicting contents
else
return CompoundAttributenamespacesa.namespaces  b.namespaces, explicita.explicit or b.explicit, enumerablea.enumerable or b.enumerable, dynamica.dynamic or b.dynamic, categorya.category  none ? a.category : b.category, overrideModa.overrideMod  none ? a.overrideMod : b.overrideMod, prototypea.prototype or b.prototype, unuseda.unused or b.unused
end if
end if
end proc;
toCompoundAttribute(a) returns a converted to a CompoundAttribute even if it was a simple namespace, true, or none.
proc toCompoundAttribute(aAttributeOptNotFalse): CompoundAttribute
case a of
{nonetruedo
return CompoundAttributenamespaces: {}, explicitfalse, enumerablefalse, dynamicfalse, categorynone, overrideModnone, prototypefalse, unusedfalse;
return CompoundAttributenamespaces: {a}, explicitfalse, enumerablefalse, dynamicfalse, categorynone, overrideModnone, prototypefalse, unusedfalse;
CompoundAttribute do return a
end case
end proc;

Access Utilities

accessesOverlap(accesses1accesses2) returns true if the two AccessSets have a nonempty intersection.
proc accessesOverlap(accesses1AccessSetaccesses2AccessSet): Boolean
return accesses1 = accesses2 or accesses1 = readWrite or accesses2 = readWrite
end proc;
proc archetype(oObject): ObjectOpt
case o of
Undefined  Null do return none;
Boolean do return Boolean.prototype;
Long do return long.prototype;
ULong do return ulong.prototype;
Float32 do return float.prototype;
Float64 do return Number.prototype;
Char16 do return char.prototype;
String do return String.prototype;
Class do return Class.prototype;
SimpleInstance  RegExp  Date  Package do return o.archetype
end case
end proc;
archetypes(o) returns the set of o’s archetypes, not including o itself.
proc archetypes(oObject): Object{}
aObjectOpt  archetype(o);
if a = none then return {} end if;
return {a archetypes(a)
end proc;
o is an object that is known to have slot id. findSlot(oid) returns that slot.
proc findSlot(oObjectidInstanceVariable): Slot
note  o must be a SimpleInstance or a MethodClosure in order to have slots.
matchingSlotsSlot{}  {s | s  o.slots such that s.id = id};
return the one element of matchingSlots
end proc;
setupVariable(v) runs Setup and initialises the type of the variable v, making sure that Setup is done at most once and does not reenter itself.
proc setupVariable(vVariable)
setup: (()  ClassOpt {nonebusy v.setup;
case setup of
()  ClassOpt do
v.setup  busy;
typeClassOpt  setup();
if type = none then type  Object end if;
v.type  type;
v.setup  none;
{nonedo nothing;
{busydo
throw a ConstantError exception — a constant’s type or initialiser cannot depend on the value of that constant
end case
end proc;
writeVariable(vnewValueclearInitializer) writes the value newValue into the mutable or immutable variable v. newValue is coerced to v’s type. If the clearInitializer flag is set, then the caller has just evaluated v’s initialiser and is supplying its result in newValue. In this case writeVariable atomically clears v.initializer while writing v.value. In all other cases the presence of an initialiser or an existing value will prevent an immutable variable’s value from being written.
proc writeVariable(vVariablenewValueObjectclearInitializerBoolean): Object
coercedValueObject  coerce(newValuev.type);
if clearInitializer then v.initializer  none end if;
if v.immutable and (v.value  none or v.initializer  nonethen
throw a ReferenceError exception — cannot initialise a const variable twice
end if;
v.value  coercedValue;
return coercedValue
end proc;

Environmental Utilities

If env is from within a class’s body, getEnclosingClass(env) returns the innermost such class; otherwise, it returns none.
proc getEnclosingClass(envEnvironment): ClassOpt
if some c  env satisfies c  Class then
Let c be the first element of env that is a Class.
return c
end if;
return none
end proc;
If env is from within a function’s body, getEnclosingParameterFrame(env) returns the ParameterFrame for the innermost such function; otherwise, it returns none.
proc getEnclosingParameterFrame(envEnvironment): ParameterFrameOpt
for each frame  env do
case frame of
LocalFrame  WithFrame do nothing;
ParameterFrame do return frame;
Package  Class do return none
end case
end for each;
return none
end proc;
getRegionalEnvironment(env) returns all frames in env up to and including the first regional frame. A regional frame is either any frame other than a with frame or local block frame, a local block frame directly enclosed in a class, or a local block frame directly enclosed in a with frame directly enclosed in a class.
proc getRegionalEnvironment(envEnvironment): Frame[]
iInteger  0;
while env[i LocalFrame  WithFrame do i  i + 1 end while;
if env[i Class then while i  0 and env[i LocalFrame do i  i – 1 end while
end if;
return env[0 ... i]
end proc;
getRegionalFrame(env) returns the most specific regional frame in env.
proc getRegionalFrame(envEnvironment): Frame
regionalEnvFrame[]  getRegionalEnvironment(env);
return regionalEnv[|regionalEnv| – 1]
end proc;
getPackageFrame(env) returns the innermost package frame in env.
proc getPackageFrame(envEnvironment): Package
iInteger  0;
while env[i Package do i  i + 1 end while;
note  Every environment ends with a Package frame, so one will always be found.
return env[i]
end proc;

Property Lookup

findLocalSingletonProperty(omultinameaccess) looks in o for a local singleton property with one of the names in multiname and access that includes access. If there is no such property, findLocalSingletonProperty returns none. If there is exactly one such property, findLocalSingletonProperty returns it. If there is more than one such property, findLocalSingletonProperty throws an error.
proc findLocalSingletonProperty(oNonWithFrame  SimpleInstance  RegExp  Date, multinameMultiname, accessAccess): SingletonPropertyOpt
matchingLocalBindingsLocalBinding{}  {b | b  o.localBindings such that b.qname  multiname and accessesOverlap(b.accessesaccess)};
note  If the same property was found via several different bindings b, then it will appear only once in the set matchingProperties.
matchingPropertiesSingletonProperty{}  {b.content | b  matchingLocalBindings};
if matchingProperties = {} then return none
elsif |matchingProperties| = 1 then return the one element of matchingProperties
else
throw a ReferenceError exception — this access is ambiguous because the bindings it found belong to several different local properties
end if
end proc;
instancePropertyAccesses(m) returns instance property’s AccessSet.
proc instancePropertyAccesses(mInstanceProperty): AccessSet
case m of
InstanceVariable  InstanceMethod do return readWrite;
InstanceGetter do return read;
InstanceSetter do return write
end case
end proc;
findLocalInstanceProperty(cmultinameaccesses) looks in class c for a local instance property with one of the names in multiname and accesses that have a nonempty intersection with accesses. If there is no such property, findLocalInstanceProperty returns none. If there is exactly one such property, findLocalInstanceProperty returns it. If there is more than one such property, findLocalInstanceProperty throws an error.
proc findLocalInstanceProperty(cClassmultinameMultinameaccessesAccessSet): InstancePropertyOpt
matchesInstanceProperty{}  {m | m  c.instanceProperties such that m.multiname  multiname  {} and accessesOverlap(instancePropertyAccesses(m), accesses)};
if matches = {} then return none
elsif |matches| = 1 then return the one element of matches
else
throw a ReferenceError exception — this access is ambiguous because it found several different instance properties in the same class
end if
end proc;
findArchetypeProperty(omultinameaccessflat) looks in object o for any local or inherited property with one of the names in multiname and access that includes access. If flat is true, then properties inherited from the archetype are not considered in the search. If it finds no property, findArchetypeProperty returns none. If it finds one property, findArchetypeProperty returns it. If it finds more than one property, findArchetypeProperty prefers the more local one in the list of o’s superclasses or archetypes; if two or more properties remain, the singleton one is preferred; if two or more properties still remain, findArchetypeProperty throws an error.
Note that findArchetypeProperty(omultinameaccessflat) searches o itself rather than o’s class for properties. findArchetypeProperty will not find instance properties unless o is a class.
proc findArchetypeProperty(oObjectmultinameMultinameaccessAccessflatBoolean): PropertyOpt
case o of
Undefined  Null  Boolean  Long  ULong  Float32  Float64  Char16  String  Namespace  CompoundAttribute  MethodClosure do
m  none;
SimpleInstance  RegExp  Date  Package do
m  findLocalSingletonProperty(omultinameaccess);
Class do m  findClassProperty(omultinameaccess)
end case;
if m  none then return m end if;
if flat then return none end if;
aObjectOpt  archetype(o);
if a = none then return none end if;
return findArchetypeProperty(amultinameaccessflat)
end proc;
proc findClassProperty(cClassmultinameMultinameaccessAccess): PropertyOpt
mPropertyOpt  findLocalSingletonProperty(cmultinameaccess);
if m = none then
m  findLocalInstanceProperty(cmultinameaccess);
if m = none then
superClassOpt  c.super;
if super  none then m  findClassProperty(supermultinameaccessend if
end if
end if;
return m
end proc;
findBaseInstanceProperty(cmultinameaccesses) looks in class c and its ancestors for an instance property with one of the names in multiname and accesses that have a nonempty intersection with accesses. If there is no such property, findBaseInstanceProperty returns none. If there is exactly one such property, findBaseInstanceProperty returns it. If there is more than one such property, findBaseInstanceProperty prefers the one defined in the least specific class; if two or more properties still remain, findBaseInstanceProperty throws an error.
proc findBaseInstanceProperty(cClassmultinameMultinameaccessesAccessSet): InstancePropertyOpt
note  Start from the root class (Object) and proceed through more specific classes that are ancestors of c.
for each s  ancestors(cdo
mInstancePropertyOpt  findLocalInstanceProperty(smultinameaccesses);
if m  none then return m end if
end for each;
return none
end proc;
getDerivedInstanceProperty(cmBaseaccesses) returns the most derived instance property whose name includes that of mBase and whose accesses that have a nonempty intersection with accesses. The caller of getDerivedInstanceProperty ensures that such an instance property always exists. If accesses is readWrite then it is possible that this search could find both a getter and a setter defined in the same class; in this case either the getter or the setter is returned at the implementation’s discretion.
proc getDerivedInstanceProperty(cClassmBaseInstancePropertyaccessesAccessSet): InstanceProperty
if some m  c.instanceProperties satisfies mBase.multiname  m.multiname and accessesOverlap(instancePropertyAccesses(m), accessesthen
return m
else return getDerivedInstanceProperty(c.supermBaseaccesses)
end if
end proc;
readImplicitThis(env) returns the value of implicit this to be used to access instance properties within a class’s scope without using the . operator. An implicit this is well-defined only inside instance methods and constructors; readImplicitThis throws an error if there is no well-defined implicit this value or if an attempt is made to read it before it has been initialised.
proc readImplicitThis(envEnvironment): Object
frameParameterFrameOpt  getEnclosingParameterFrame(env);
if frame = none then
throw a ReferenceError exception — can’t access instance properties outside an instance method without supplying an instance object
end if;
thisObjectOpt  frame.this;
if this = none then
throw a ReferenceError exception — can’t access instance properties inside a non-instance method without supplying an instance object
end if;
if frame.kind  {instanceFunctionconstructorFunctionthen
throw a ReferenceError exception — can’t access instance properties inside a non-instance method without supplying an instance object
end if;
if not frame.superconstructorCalled then
throw an UninitializedError exception — can’t access instance properties from within a constructor before the superconstructor has been called
end if;
return this
end proc;
hasProperty(opropertyflatphase) returns true if o has a readable or writable property named property. If flat is true, then properties inherited from the archetype are not considered.
proc hasProperty(oObjectpropertyObjectflatBooleanphasePhase): Boolean
cClass  objectType(o);
return c.hasProperty(ocpropertyflatphase)
end proc;
hasProperty(ocpropertyflatphase) is the implementation of hasProperty for a native class unless specified otherwise in the class’s definition. Host classes may either also use ordinaryHasProperty or define a different procedure to perform this test. c is o’s type.
proc ordinaryHasProperty(oObject, cClass, propertyObject, flatBoolean, phasePhase): Boolean
qnameQualifiedName  objectToQualifiedName(propertyphase);
return findBaseInstanceProperty(c, {qname}, read none or findBaseInstanceProperty(c, {qname}, write none or findArchetypeProperty(o, {qname}, readflat none or findArchetypeProperty(o, {qname}, writeflat none
end proc;

Reading

If r is an Object, readReference(rphase) returns it unchanged. If r is a Reference, readReference reads r and returns the result. If phase is compile, only constant expressions can be evaluated in the process of reading r.
proc readReference(rObjOrRefphasePhase): Object
resultObjectOpt;
case r of
Object do result  r;
LexicalReference do result  lexicalRead(r.envr.variableMultinamephase);
result  r.limit.read(r.baser.limitr.multinamenonetruephase);
result  r.limit.bracketRead(r.baser.limitr.argstruephase)
end case;
if result  none then return result
else
throw a ReferenceError exception — property not found, and no default value is available
end if
end proc;
dotRead(omultinamephase) reads and returns the value of the multiname property of o. dotRead throws an error if the property does not exist and no default value was available for it.
proc dotRead(oObjectmultinameMultinamephasePhase): Object
limitClass  objectType(o);
resultObjectOpt  limit.read(olimitmultinamenonetruephase);
if result = none then
throw a ReferenceError exception — property not found, and no default value is available
end if;
return result
end proc;
readLength(ophase) reads and returns the value of the length property of o, ensuring that it is an integer between 0 and arrayLimit inclusive.
proc readLength(oObjectphasePhase): Integer
valueObject  dotRead(o, {public::“length”}, phase);
if value  GeneralNumber then throw a TypeError exception — length not an integer
end if;
lengthIntegerOpt  checkInteger(value);
if length = none then throw a RangeError exception — length not an integer
elsif 0  length  arrayLimit then return length
else throw a RangeError exception — length out of range
end if
end proc;
indexRead(oiphase) returns the value of o[i] or none if no such property was found; unlike dotRead, indexRead does not return a default value for missing properties. i should always be a valid array index.
proc indexRead(oObjectiIntegerphasePhase): ObjectOpt
note  0  i < arrayLimit;
limitClass  objectType(o);
xFloat64  if64;
resultObjectOpt  limit.bracketRead(olimit[x]falsephase);
if result  none and not hasProperty(oxtruephasethen
At the implementation’s discretion either do nothing, set result to none, or throw a ReferenceError.
end if;
return result
end proc;
ordinaryBracketRead(olimitargsundefinedIfMissingphase) evaluates the expression o[args] when o is a native object. Host objects may either also use ordinaryBracketRead or choose a different procedure P to evaluate o[args] by writing P into objectType(o).bracketRead.
limit is used to handle the expression super(o)[args], in which case limit is the superclass of the class inside which the super expression appears. Otherwise, limit is set to objectType(o).
proc ordinaryBracketRead(oObject, limitClass, argsObject[], undefinedIfMissingBoolean, phasePhase): ObjectOpt
if |args 1 then
throw an ArgumentError exception — exactly one argument must be supplied
end if;
qnameQualifiedName  objectToQualifiedName(args[0], phase);
return limit.read(olimit, {qname}, noneundefinedIfMissingphase)
end proc;
proc lexicalRead(envEnvironmentmultinameMultinamephasePhase): Object
iInteger  0;
while i < |envdo
frameFrame  env[i];
resultObjectOpt  none;
case frame of
Package  Class do
limitClass  objectType(frame);
result  limit.read(framelimitmultinameenvfalsephase);
ParameterFrame  LocalFrame do
mSingletonPropertyOpt  findLocalSingletonProperty(framemultinameread);
if m  none then result  readSingletonProperty(mphaseend if;
valueObjectOpt  frame.value;
if value = none then
case phase of
{compiledo
throw a ConstantError exception — cannot read a with statement’s frame from a constant expression;
{rundo
throw an UninitializedError exception — cannot read a with statement’s frame before that statement’s expression has been evaluated
end case
end if;
limitClass  objectType(value);
result  limit.read(valuelimitmultinameenvfalsephase)
end case;
if result  none then return result end if;
i  i + 1
end while;
throw a ReferenceError exception — no property found with the name multiname
end proc;
proc ordinaryRead(oObject, limitClass, multinameMultiname, envEnvironmentOpt, undefinedIfMissingBoolean, phasePhase): ObjectOpt
mBaseInstancePropertyOpt  findBaseInstanceProperty(limitmultinameread);
if mBase  none then return readInstanceProperty(olimitmBasephaseend if;
if limit  objectType(othen return none end if;
flatBoolean  env  none and o  Class;
mPropertyOpt  findArchetypeProperty(omultinamereadflat);
case m of
{nonedo
if undefinedIfMissing and o  SimpleInstance  Date  RegExp  Package and not o.sealed then
case phase of
{compiledo
throw a ConstantError exception — a constant expression cannot read dynamic properties;
{rundo return undefined
end case
else return none
end if;
SingletonProperty do return readSingletonProperty(mphase);
if o  Class or env = none then
throw a ReferenceError exception — cannot read an instance property without supplying an instance
end if;
thisObject  readImplicitThis(env);
return readInstanceProperty(thisobjectType(this), mphase)
end case
end proc;
readInstanceProperty(oqnamephase) is a simplified interface to ordinaryRead used to read instance slots that are known to exist.
proc readInstanceSlot(oObjectqnameQualifiedNamephasePhase): Object
cClass  objectType(o);
mBaseInstancePropertyOpt  findBaseInstanceProperty(c, {qname}, read);
note  readInstanceProperty is only called in cases where the instance property is known to exist, so mBase cannot be none here.
return readInstanceProperty(ocmBasephase)
end proc;
proc readInstanceProperty(thisObjectcClassmBaseInstancePropertyphasePhase): Object
mInstanceProperty  getDerivedInstanceProperty(cmBaseread);
case m of
if phase = compile and not m.immutable then
throw a ConstantError exception — a constant expression cannot read mutable variables
end if;
vObjectOpt  findSlot(thism).value;
if v = none then
case phase of
{compiledo
throw a ConstantError exception — cannot read uninitalised const variables from a constant expression;
{rundo
throw an UninitializedError exception — cannot read a const instance variable before it is initialised
end case
end if;
return v;
slotsSlot{}  {new SlotidivarFunctionLengthvalue: (m.length)f64};
return MethodClosurethisthismethodmslotsslots;
InstanceGetter do return m.call(thisphase);
m cannot be an InstanceSetter because these are only represented as write-only properties.
end case
end proc;
proc readSingletonProperty(mSingletonPropertyphasePhase): Object
case m of
{forbiddendo
throw a ReferenceError exception — cannot access a property defined in a scope outside the current region if any block inside the current region shadows it;
if phase = compile then
throw a ConstantError exception — a constant expression cannot read mutable variables
end if;
valueObject  UninstantiatedFunction  m.value;
note  value can be an UninstantiatedFunction only during the compile phase, which was ruled out above.
return value;
if phase = compile and not m.immutable then
throw a ConstantError exception — a constant expression cannot read mutable variables
end if;
valueVariableValue  m.value;
case value of
Object do return value;
{nonedo
if not m.immutable then throw an UninitializedError exception end if;
note  Try to run a const variable’s initialiser if there is one.
Evaluate setupVariable(m) and ignore its result;
initializerInitializer  {nonebusy m.initializer;
if initializer  {nonebusythen
case phase of
{compiledo
throw a ConstantError exception — a constant expression cannot access a constant with a missing or recursive initialiser;
{rundo throw an UninitializedError exception
end case
end if;
m.initializer  busy;
coercedValueObject;
try
newValueObject  initializer(m.initializerEnvcompile);
coercedValue  writeVariable(mnewValuetrue)
catch xSemanticException do
note  If initialisation failed, restore m.initializer to its original value so it can be tried later.
m.initializer  initializer;
throw x
end try;
return coercedValue;
note  An uninstantiated function can only be found when phase = compile.
throw a ConstantError exception — an uninstantiated function is not a constant expression
end case;
Getter do
envEnvironmentOpt  m.env;
if env = none then
note  An uninstantiated getter can only be found when phase = compile.
throw a ConstantError exception — an uninstantiated getter is not a constant expression
end if;
return m.call(envphase);
Setter do
m cannot be a Setter because these are only represented as write-only properties.
end case
end proc;

Writing

If r is a reference, writeReference(rnewValue) writes newValue into r. An error occurs if r is not a reference. writeReference is never called from a constant expression.
proc writeReference(rObjOrRefnewValueObjectphase: {run})
result: {noneok};
case r of
Object do
throw a ReferenceError exception — a non-reference is not a valid target of an assignment;
Evaluate lexicalWrite(r.envr.variableMultinamenewValuenot r.strictphase) and ignore its result;
result  ok;
result  r.limit.write(r.baser.limitr.multinamenonenewValuetruephase);
result  r.limit.bracketWrite(r.baser.limitr.argsnewValuetruephase)
end case;
if result = none then
throw a ReferenceError exception — property not found and could not be created
end if
end proc;
dotWrite(omultinamenewValuephase) is a simplified interface to write newValue into the multiname property of o.
proc dotWrite(oObjectmultinameMultinamenewValueObjectphase: {run})
limitClass  objectType(o);
result: {noneok limit.write(olimitmultinamenonenewValuetruephase);
if result = none then
throw a ReferenceError exception — property not found and could not be created
end if
end proc;
writeLength(olengthphase) ensures that length is between 0 and arrayLimit inclusive and then writes it into the length property of o. Note that if o is an Array, the act of writing its length property will invoke the Array_setLength setter.
proc writeLength(oObjectlengthIntegerphase: {run})
if length < 0 or length > arrayLimit then
throw a RangeError exception — length out of range
end if;
Evaluate dotWrite(o, {public::“length”}, lengthf64phase) and ignore its result
end proc;
proc indexWrite(oObjectiIntegernewValueObjectOptphase: {run})
if i < 0 or i  arrayLimit then throw a RangeError exception — index out of range
end if;
limitClass  objectType(o);
if newValue = none then
deleteResultBooleanOpt  limit.bracketDelete(olimit[if64]phase);
if deleteResult = false then
throw a ReferenceError exception — cannot delete element
end if
else
writeResult: {noneok limit.bracketWrite(olimit[if64]newValuetruephase);
if writeResult = none then
throw a ReferenceError exception — element not found and could not be created
end if
end if
end proc;
proc ordinaryBracketWrite(oObject, limitClass, argsObject[], newValueObject, createIfMissingBoolean, phase: {run}): {noneok}
if |args 1 then
throw an ArgumentError exception — exactly one argument must be supplied
end if;
qnameQualifiedName  objectToQualifiedName(args[0], phase);
return limit.write(olimit, {qname}, nonenewValuecreateIfMissingphase)
end proc;
proc lexicalWrite(envEnvironment, multinameMultiname, newValueObject, createIfMissingBoolean, phase: {run})
iInteger  0;
while i < |envdo
frameFrame  env[i];
result: {noneok none;
case frame of
Package  Class do
limitClass  objectType(frame);
result  limit.write(framelimitmultinameenvnewValuefalsephase);
ParameterFrame  LocalFrame do
mSingletonPropertyOpt  findLocalSingletonProperty(framemultinamewrite);
if m  none then
Evaluate writeSingletonProperty(mnewValuephase) and ignore its result;
result  ok
end if;
valueObjectOpt  frame.value;
if value = none then
throw an UninitializedError exception — cannot read a with statement’s frame before that statement’s expression has been evaluated
end if;
limitClass  objectType(value);
result  limit.write(valuelimitmultinameenvnewValuefalsephase)
end case;
if result = ok then return end if;
i  i + 1
end while;
if createIfMissing then
pkgPackage  getPackageFrame(env);
note  Try to write the variable into pkg again, this time allowing new dynamic bindings to be created dynamically.
limitClass  objectType(pkg);
result: {noneok limit.write(pkglimitmultinameenvnewValuetruephase);
if result = ok then return end if
end if;
throw a ReferenceError exception — no existing property found with the name multiname and one could not be created
end proc;
proc ordinaryWrite(oObject, limitClass, multinameMultiname, envEnvironmentOpt, newValueObject, createIfMissingBoolean, phase: {run}): {noneok}
mBaseInstancePropertyOpt  findBaseInstanceProperty(limitmultinamewrite);
if mBase  none then
Evaluate writeInstanceProperty(olimitmBasenewValuephase) and ignore its result;
return ok
end if;
if limit  objectType(othen return none end if;
mPropertyOpt  findArchetypeProperty(omultinamewritetrue);
case m of
{nonedo
if createIfMissing and o  SimpleInstance  Date  RegExp  Package and not o.sealed and (some qname  multiname satisfies qname.namespace = publicthen
note  Before trying to create a new dynamic property named qname, check that there is no read-only fixed property with the same name.
if findBaseInstanceProperty(objectType(o), {qname}, read) = none and findArchetypeProperty(o, {qname}, readtrue) = none then
Evaluate createDynamicProperty(oqnamefalsetruenewValue) and ignore its result;
return ok
end if
end if;
return none;
Evaluate writeSingletonProperty(mnewValuephase) and ignore its result;
return ok;
if o  Class or env = none then
throw a ReferenceError exception — cannot write an instance property without supplying an instance
end if;
thisObject  readImplicitThis(env);
Evaluate writeInstanceProperty(thisobjectType(this), mnewValuephase) and ignore its result;
return ok
end case
end proc;
The caller must make sure that the created property does not already exist and does not conflict with any other property.
proc createDynamicProperty(oSimpleInstance  Date  RegExp  Package, qnameQualifiedName, sealedBoolean, enumerableBoolean, newValueObject)
dvDynamicVar  new DynamicVarvaluenewValuesealedsealed;
o.localBindings  o.localBindings  {LocalBindingqnameqname, accessesreadWrite, explicitfalse, enumerableenumerable, contentdv}
end proc;
proc writeInstanceProperty(thisObject, cClass, mBaseInstanceProperty, newValueObject, phase: {run})
mInstanceProperty  getDerivedInstanceProperty(cmBasewrite);
case m of
sSlot  findSlot(thism);
coercedValueObject  coerce(newValuem.type);
if m.immutable and s.value  none then
throw a ReferenceError exception — cannot initialise a const instance variable twice
end if;
s.value  coercedValue;
throw a ReferenceError exception — cannot write to an instance method;
m cannot be an InstanceGetter because these are only represented as read-only properties.
InstanceSetter do Evaluate m.call(thisnewValuephase) and ignore its result
end case
end proc;
proc writeSingletonProperty(mSingletonPropertynewValueObjectphase: {run})
case m of
{forbiddendo
throw a ReferenceError exception — cannot access a property defined in a scope outside the current region if any block inside the current region shadows it;
Variable do Evaluate writeVariable(mnewValuefalse) and ignore its result;
DynamicVar do m.value  newValue;
Getter do
m cannot be a Getter because these are only represented as read-only properties.
Setter do
envEnvironmentOpt  m.env;
note  All instances are resolved for the run phase, so env  none.
Evaluate m.call(newValueenvphase) and ignore its result
end case
end proc;

Deleting

If r is a Reference, deleteReference(r) deletes it. If r is an Object, this function signals an error in strict mode or returns true in non-strict mode. deleteReference is never called from a constant expression.
proc deleteReference(rObjOrRefstrictBooleanphase: {run}): Boolean
resultBooleanOpt;
case r of
Object do
if strict then
throw a ReferenceError exception — a non-reference is not a valid target for delete in strict mode
else result  true
end if;
LexicalReference do result  lexicalDelete(r.envr.variableMultinamephase);
result  r.limit.delete(r.baser.limitr.multinamenonephase);
result  r.limit.bracketDelete(r.baser.limitr.argsphase)
end case;
if result  none then return result else return true end if
end proc;
proc ordinaryBracketDelete(oObjectlimitClassargsObject[], phase: {run}): BooleanOpt
if |args 1 then
throw an ArgumentError exception — exactly one argument must be supplied
end if;
qnameQualifiedName  objectToQualifiedName(args[0], phase);
return limit.delete(olimit, {qname}, nonephase)
end proc;
proc lexicalDelete(envEnvironmentmultinameMultinamephase: {run}): Boolean
iInteger  0;
while i < |envdo
frameFrame  env[i];
resultBooleanOpt  none;
case frame of
Package  Class do
limitClass  objectType(frame);
result  limit.delete(framelimitmultinameenvphase);
ParameterFrame  LocalFrame do
if findLocalSingletonProperty(framemultinamewrite none then
result  false
end if;
valueObjectOpt  frame.value;
if value = none then
throw an UninitializedError exception — cannot read a with statement’s frame before that statement’s expression has been evaluated
end if;
limitClass  objectType(value);
result  limit.delete(valuelimitmultinameenvphase)
end case;
if result  none then return result end if;
i  i + 1
end while;
return true
end proc;
proc ordinaryDelete(oObject, limitClass, multinameMultiname, envEnvironmentOpt, phase: {run}): BooleanOpt
if findBaseInstanceProperty(limitmultinamewrite none then return false end if;
if limit  objectType(othen return none end if;
mPropertyOpt  findArchetypeProperty(omultinamewritetrue);
case m of
{nonedo return none;
{forbiddendo
throw a ReferenceError exception — cannot access a property defined in a scope outside the current region if any block inside the current region shadows it;
Variable  Getter  Setter do return false;
if m.sealed then return false
else
o.localBindings  {b | b  o.localBindings such that b.qname  multiname or b.content  m};
return true
end if;
if o  Class or env = none then return false end if;
Evaluate readImplicitThis(env) and ignore its result;
return false
end case
end proc;

Enumerating

proc ordinaryEnumerate(oObject): Object{}
e1Object{}  enumerateInstanceProperties(objectType(o));
e2Object{}  enumerateArchetypeProperties(o);
return e1  e2
end proc;
proc enumerateInstanceProperties(cClass): Object{}
eObject{}  {};
for each m  c.instanceProperties do
if m.enumerable then
e  e  {qname.id | qname  m.multiname such that qname.namespace = public}
end if
end for each;
superClassOpt  c.super;
if super = none then return e
else return e  enumerateInstanceProperties(super)
end if
end proc;
proc enumerateArchetypeProperties(oObject): Object{}
eObject{}  {};
for each a  {o archetypes(odo
if a  BindingObject then e  e  enumerateSingletonProperties(aend if
end for each;
return e
end proc;
proc enumerateSingletonProperties(oBindingObject): Object{}
eObject{}  {};
for each b  o.localBindings do
if b.enumerable and b.qname.namespace = public then e  e  {b.qname.idend if
end for each;
if o  Class then
superClassOpt  o.super;
if super  none then e  e  enumerateSingletonProperties(superend if
end if;
return e
end proc;

Calling Instances

proc call(thisObjectaObjectargsObject[], phasePhase): Object
case a of
Undefined  Null  Boolean  GeneralNumber  Char16  String  Namespace  CompoundAttribute  Date  RegExp  Package do
throw a TypeError exception;
Class do return a.call(thisaargsphase);
f: (Object  SimpleInstance  Object[]  Phase  Object {none a.call;
if f = none then throw a TypeError exception end if;
return f(thisaargsphase);
MethodClosure do mInstanceMethod  a.methodreturn m.call(a.thisargsphase)
end case
end proc;
proc ordinaryCall(thisObjectcClassargsObject[], phasePhase): Object
note  This function can be used in a constant expression.
if not c.complete then
throw a ConstantError exception — cannot call a class before its definition has been compiled
end if;
if |args 1 then
throw an ArgumentError exception — exactly one argument must be supplied
end if;
return coerce(args[0], c)
end proc;
proc sameAsConstruct(thisObjectcClassargsObject[], phasePhase): Object
return construct(cargsphase)
end proc;

Creating Instances

proc construct(aObjectargsObject[], phasePhase): Object
case a of
Undefined  Null  Boolean  GeneralNumber  Char16  String  Namespace  CompoundAttribute  MethodClosure  Date  RegExp  Package do
throw a TypeError exception;
Class do return a.construct(aargsphase);
f: (SimpleInstance  Object[]  Phase  Object {none a.construct;
if f = none then throw a TypeError exception end if;
return f(aargsphase)
end case
end proc;
proc ordinaryConstruct(cClassargsObject[], phasePhase): Object
if not c.complete then
throw a ConstantError exception — cannot construct an instance of a class before its definition has been compiled
end if;
if phase = compile then
throw a ConstantError exception — a class constructor call is not a constant expression because it evaluates to a new object each time it is evaluated
end if;
thisSimpleInstance  createSimpleInstance(cc.prototypenonenonenone);
Evaluate callInit(thiscargsphase) and ignore its result;
return this
end proc;
proc createSimpleInstance(cClass, archetypeObjectOpt, call: (Object  SimpleInstance  Object[]  Phase  Object {none}, construct: (SimpleInstance  Object[]  Phase  Object {none}, envEnvironmentOpt): SimpleInstance
slotsSlot{}  {};
for each s  ancestors(cdo
for each m  s.instanceProperties do
if m  InstanceVariable then
slotSlot  new Slotidmvaluem.defaultValue;
slots  slots  {slot}
end if
end for each
end for each;
return new SimpleInstancelocalBindings: {}, archetypearchetype, sealednot c.dynamic, typec, slotsslots, callcall, constructconstruct, envenv
end proc;
proc callInit(thisSimpleInstancecClassOptargsObject[], phase: {run})
init: (SimpleInstance  Object[]  {run ())  {none none;
if c  none then init  c.init end if;
if init  none then Evaluate init(thisargsphase) and ignore its result
else
if args  [] then
throw an ArgumentError exception — the default constructor does not take any arguments
end if
end if
end proc;

Adding Local Definitions

proc defineSingletonProperty(envEnvironment, idString, namespacesNamespace{}, overrideModOverrideModifier, explicitBoolean, accessesAccessSet, mSingletonProperty): Multiname
innerFrameNonWithFrame  env[0];
if overrideMod  none then
throw an AttributeError exception — a local definition cannot have the override attribute
end if;
if explicit and innerFrame  Package then
throw an AttributeError exception — the explicit attribute can only be used at the top level of a package
end if;
namespaces2Namespace{}  namespaces;
if namespaces2 = {} then namespaces2  {publicend if;
multinameMultiname  {ns::id | ns  namespaces2};
regionalEnvFrame[]  getRegionalEnvironment(env);
if some b  innerFrame.localBindings satisfies b.qname  multiname and accessesOverlap(b.accessesaccessesthen
throw a DefinitionError exception — duplicate definition in the same scope
end if;
if innerFrame  Class and id = innerFrame.name then
throw a DefinitionError exception — a static property of a class cannot have the same name as the class, regardless of the namespace
end if;
for each frame  regionalEnv[1 ...] do
if frame  WithFrame and (some b  frame.localBindings satisfies b.qname  multiname and accessesOverlap(b.accessesaccessesand b.content  forbiddenthen
throw a DefinitionError exception — this definition would shadow a property defined in an outer scope within the same region
end if
end for each;
newBindingsLocalBinding{}  {LocalBindingqnameqname, accessesaccesses, explicitexplicit, enumerabletrue, contentm | qname  multiname};
innerFrame.localBindings  innerFrame.localBindings  newBindings;
note  Mark the bindings of multiname as forbidden in all non-innermost frames in the current region if they haven’t been marked as such already.
newForbiddenBindingsLocalBinding{}  {LocalBindingqnameqname, accessesaccesses, explicittrue, enumerabletrue, contentforbidden | qname  multiname};
for each frame  regionalEnv[1 ...] do
note  Since frame  Class here, a Class frame never gets a forbidden binding.
if frame  WithFrame then
frame.localBindings  frame.localBindings  newForbiddenBindings
end if
end for each;
return multiname
end proc;
defineHoistedVar(envidinitialValue) defines a hoisted variable with the name id in the environment env. Hoisted variables are hoisted to the package or enclosing function scope. Multiple hoisted variables may be defined in the same scope, but they may not coexist with non-hoisted variables with the same name. A hoisted variable can be defined using either a var or a function statement. If it is defined using var, then initialValue is always undefined (if the var statement has an initialiser, then the variable’s value will be written later when the var statement is executed). If it is defined using function, then initialValue must be a function instance or open instance. A var hoisted variable may be hoisted into the ParameterFrame if there is already a parameter with the same name; a function hoisted variable is never hoisted into the ParameterFrame and will shadow a parameter with the same name for compatibility with ECMAScript Edition 3. If there are multiple function definitions, the initial value is the last function definition.
proc defineHoistedVar(envEnvironment, idString, initialValueObject  UninstantiatedFunction): DynamicVar
qnameQualifiedName  public::id;
regionalEnvFrame[]  getRegionalEnvironment(env);
regionalFrameFrame  regionalEnv[|regionalEnv| – 1];
note  env is either a Package or a ParameterFrame because hoisting only occurs into package or function scope.
existingBindingsLocalBinding{}  {b | b  regionalFrame.localBindings such that b.qname = qname};
if (existingBindings = {} or initialValue  undefinedand regionalFrame  ParameterFrame and |regionalEnv 2 then
regionalFrame  regionalEnv[|regionalEnv| – 2];
existingBindings  {b | b  regionalFrame.localBindings such that b.qname = qname}
end if;
if existingBindings = {} then
vDynamicVar  new DynamicVarvalueinitialValuesealedtrue;
regionalFrame.localBindings  regionalFrame.localBindings  {LocalBindingqnameqname, accessesreadWrite, explicitfalse, enumerabletrue, contentv};
return v
elsif |existingBindings 1 then
throw a DefinitionError exception — a hoisted definition conflicts with a non-hoisted one
else
bLocalBinding  the one element of existingBindings;
mSingletonProperty  b.content;
if b.accesses  readWrite or m  DynamicVar then
throw a DefinitionError exception — a hoisted definition conflicts with a non-hoisted one
end if;
note  At this point a hoisted binding of the same var already exists, so there is no need to create another one. Overwrite its initial value if the new definition is a function definition.
if initialValue  undefined then m.value  initialValue end if;
m.sealed  true;
regionalFrame.localBindings  regionalFrame.localBindings – {b};
regionalFrame.localBindings  regionalFrame.localBindings  {LocalBindingenumerabletrue, other fields from b};
return m
end if
end proc;

Adding Instance Definitions

proc searchForOverrides(cClassmultinameMultinameaccessesAccessSet): InstancePropertyOpt
mBaseInstancePropertyOpt  none;
sClassOpt  c.super;
if s  none then
for each qname  multiname do
mInstancePropertyOpt  findBaseInstanceProperty(s, {qname}, accesses);
if mBase = none then mBase  m
elsif m  none and m  mBase then
throw a DefinitionError exception — cannot override two separate superclass methods at the same time
end if
end for each
end if;
return mBase
end proc;
proc defineInstanceProperty(cClass, cxtContext, idString, namespacesNamespace{}, overrideModOverrideModifier, explicitBoolean, mInstanceProperty): InstancePropertyOpt
if explicit then
throw an AttributeError exception — the explicit attribute can only be used at the top level of a package
end if;
accessesAccessSet  instancePropertyAccesses(m);
requestedMultinameMultiname  {ns::id | ns  namespaces};
openMultinameMultiname  {ns::id | ns  cxt.openNamespaces};
definedMultinameMultiname;
searchedMultinameMultiname;
if requestedMultiname = {} then
definedMultiname  {public::id};
searchedMultiname  openMultiname;
note  definedMultiname  searchedMultiname because the public namespace is always open.
else definedMultiname  requestedMultinamesearchedMultiname  requestedMultiname
end if;
mBaseInstancePropertyOpt  searchForOverrides(csearchedMultinameaccesses);
mOverriddenInstancePropertyOpt  none;
if mBase  none then
mOverridden  getDerivedInstanceProperty(cmBaseaccesses);
definedMultiname  mOverridden.multiname;
if not (requestedMultiname  definedMultinamethen
throw a DefinitionError exception — cannot extend the set of a property’s namespaces when overriding it
end if;
goodKindBoolean;
case m of
InstanceVariable do goodKind  mOverridden  InstanceVariable;
goodKind  mOverridden  InstanceVariable  InstanceGetter;
goodKind  mOverridden  InstanceVariable  InstanceSetter;
InstanceMethod do goodKind  mOverridden  InstanceMethod
end case;
if not goodKind then
throw a DefinitionError exception — a method can override only another method, a variable can override only another variable, a getter can override only a getter or a variable, and a setter can override only a setter or a variable
end if;
if mOverridden.final then
throw a DefinitionError exception — cannot override a final property
end if
end if;
if some m2  c.instanceProperties satisfies m2.multiname  definedMultiname  {} and accessesOverlap(instancePropertyAccesses(m2), accessesthen
throw a DefinitionError exception — duplicate definition in the same scope
end if;
case overrideMod of
{nonedo
if mBase  none then
throw a DefinitionError exception — a definition that overrides a superclass’s property must be marked with the override attribute
end if;
if searchForOverrides(copenMultinameaccesses none then
throw a DefinitionError exception — this definition is hidden by one in a superclass when accessed without a namespace qualifier; in the rare cases where this is intentional, use the override(false) attribute
end if;
{falsedo
if mBase  none then
throw a DefinitionError exception — this definition is marked with override(false) but it overrides a superclass’s property
end if;
{truedo
if mBase = none then
throw a DefinitionError exception — this definition is marked with override or override(true) but it doesn’t override a superclass’s property
end if;
{undefineddo nothing
end case;
m.multiname  definedMultiname;
c.instanceProperties  c.instanceProperties  {m};
return mOverridden
end proc;

Instantiation

proc instantiateFunction(ufUninstantiatedFunctionenvEnvironment): SimpleInstance
cClass  uf.type;
iSimpleInstance  createSimpleInstance(cc.prototypeuf.calluf.constructenv);
Evaluate dotWrite(i, {public::“length”}, (uf.length)f64run) and ignore its result;
if c = PrototypeFunction then
prototypeObject  construct(Object[]run);
Evaluate dotWrite(prototype, {public::“constructor”}, irun) and ignore its result;
Evaluate dotWrite(i, {public::“prototype”}, prototyperun) and ignore its result
end if;
instantiationsSimpleInstance{}  uf.instantiations;
if instantiations  {} then
Suppose that instantiateFunction were to choose at its discretion some element i2 of instantiations, assign i2.env  env, and return i. If the behaviour of doing that assignment were observationally indistinguishable by the rest of the program from the behaviour of returning i without modifying i2.env, then the implementation may, but does not have to, return i2 now, discarding (or not even bothering to create) the value of i.
note  The above rule allows an implementation to avoid creating a fresh closure each time a local function is instantiated if it can show that the closures would behave identically. This optimisation is not transparent to the programmer because the instantiations will be === to each other and share one set of properties (including the prototype property, if applicable) rather than each having its own. ECMAScript programs should not rely on this distinction.
end if;
uf.instantiations  instantiations  {i};
return i
end proc;
proc instantiateProperty(mSingletonPropertyenvEnvironment): SingletonProperty
case m of
{forbiddendo return m;
note  m.setup = none because Setup must have been called on a frame before that frame can be instantiated.
valueVariableValue  m.value;
if value  UninstantiatedFunction then
value  instantiateFunction(valueenv)
end if;
return new Variabletypem.type, valuevalue, immutablem.immutable, setupnone, initializerm.initializer, initializerEnvenv;
valueObject  UninstantiatedFunction  m.value;
if value  UninstantiatedFunction then
value  instantiateFunction(valueenv)
end if;
return new DynamicVarvaluevaluesealedm.sealed;
Getter do
case m.env of
Environment do return m;
{nonedo return new Gettercallm.callenvenv
end case;
Setter do
case m.env of
Environment do return m;
{nonedo return new Settercallm.callenvenv
end case
end case
end proc;
tuple PropertyTranslation
end tuple;
proc instantiateLocalFrame(frameLocalFrameenvEnvironment): LocalFrame
instantiatedFrameLocalFrame  new LocalFramelocalBindings: {};
propertiesSingletonProperty{}  {b.content | b  frame.localBindings};
propertyTranslationsPropertyTranslation{}  {PropertyTranslationfromm, toinstantiateProperty(m[instantiatedFrame]  env) | m  properties};
proc translateProperty(mSingletonProperty): SingletonProperty
miPropertyTranslation  the one element mi  propertyTranslations that satisfies mi.from = m;
return mi.to
end proc;
instantiatedFrame.localBindings  {LocalBindingcontenttranslateProperty(b.content), other fields from b | b  frame.localBindings};
return instantiatedFrame
end proc;
proc instantiateParameterFrame(frameParameterFrame, envEnvironment, singularThisObjectOpt): ParameterFrame
note  frame.superconstructorCalled must be true if and only if frame.kind is not constructorFunction.
instantiatedFrameParameterFrame  new ParameterFramelocalBindings: {}, kindframe.kind, handlingframe.handling, callsSuperconstructorframe.callsSuperconstructor, superconstructorCalledframe.superconstructorCalled, thissingularThis, returnTypeframe.returnType;
note  properties will contain the set of all SingletonProperty records found in the frame.
propertiesSingletonProperty{}  {b.content | b  frame.localBindings};
note  If any of the parameters (including the rest parameter) are anonymous, their bindings will not be present in frame.localBindings. In this situation, the following steps add their SingletonProperty records to properties.
for each p  frame.parameters do properties  properties  {p.varend for each;
restVariableOpt  frame.rest;
if rest  none then properties  properties  {restend if;
propertyTranslationsPropertyTranslation{}  {PropertyTranslationfromm, toinstantiateProperty(m[instantiatedFrame]  env) | m  properties};
proc translateProperty(mSingletonProperty): SingletonProperty
miPropertyTranslation  the one element mi  propertyTranslations that satisfies mi.from = m;
return mi.to
end proc;
instantiatedFrame.localBindings  {LocalBindingcontenttranslateProperty(b.content), other fields from b | b  frame.localBindings};
instantiatedFrame.parameters  [ParametervartranslateProperty(op.var), defaultop.default | op  frame.parameters];
if rest = none then instantiatedFrame.rest  none
else instantiatedFrame.rest  translateProperty(rest)
end if;
return instantiatedFrame
end proc;

Sealing

proc sealObject(oObject)
if o  SimpleInstance  RegExp  Date  Package then o.sealed  true end if
end proc;
proc sealAllLocalProperties(oObject)
if o  BindingObject then
for each b  o.localBindings do
mSingletonProperty  b.content;
if m  DynamicVar then m.sealed  true end if
end for each
end if
end proc;
proc sealLocalProperty(oObjectqnameQualifiedName)
cClass  objectType(o);
if findBaseInstanceProperty(c, {qname}, read) = none and findBaseInstanceProperty(c, {qname}, write) = none and o  BindingObject then
matchingPropertiesSingletonProperty{}  {b.content | b  o.localBindings such that b.qname = qname};
for each m  matchingProperties do
if m  DynamicVar then m.sealed  true end if
end for each
end if
end proc;

Standard Class Utilities

proc defaultArg(argsObject[], nIntegerdefaultObject): Object
if n  |argsthen return default end if;
argObject  args[n];
if arg = undefined then return default else return arg end if
end proc;
proc stdConstBinding(qnameQualifiedNametypeClassvalueObject): LocalBinding
return LocalBindingqnameqname, accessesreadWrite, explicitfalse, enumerablefalse, content: new Variabletypetype, valuevalue, immutabletrue, setupnone, initializernone
end proc;
proc stdExplicitConstBinding(qnameQualifiedNametypeClassvalueObject): LocalBinding
return LocalBindingqnameqname, accessesreadWrite, explicittrue, enumerablefalse, content: new Variabletypetype, valuevalue, immutabletrue, setupnone, initializernone
end proc;
proc stdVarBinding(qnameQualifiedNametypeClassvalueObject): LocalBinding
return LocalBindingqnameqname, accessesreadWrite, explicitfalse, enumerablefalse, content: new Variabletypetype, valuevalue, immutablefalse, setupnone, initializernone
end proc;
proc stdFunction(qnameQualifiedName, callObject  SimpleInstance  Object[]  Phase  Object, lengthInteger): LocalBinding
slotsSlot{}  {new SlotidivarFunctionLengthvaluelengthf64};
fSimpleInstance  new SimpleInstancelocalBindings: {}, archetypeFunctionPrototype, sealedtrue, typeFunction, slotsslots, callcall, constructnone, envnone;
return LocalBindingqnameqname, accessesreadWrite, explicitfalse, enumerablefalse, content: new VariabletypeFunction, valuef, immutabletrue, setupnone, initializernone
end proc;
stdReserve(qnamearchetype) is used during the creation of system objects. It returns an alias of the local binding of qname in archetype, which should be the archetype of the object being created. The alias that stdReserve defines serves to prevent qname from being later redefined by users in the object being created while at the same time retaining the definition of qname that would normally be inherited from archetype.
proc stdReserve(qnameQualifiedNamearchetypeSimpleInstance): LocalBinding
matchingBindingsLocalBinding{}  {b | b  archetype.localBindings such that b.qname = qname};
return the one element of matchingBindings
end proc;

Expressions

Syntax

  {allowInnoIn}

Terminal Actions

Name[Identifier]: String;
Value[Number]: GeneralNumber;
Value[String]: String;
Body[RegularExpression]: String;
Flags[RegularExpression]: String;

Identifiers

Syntax

Identifier 
   Identifier
|  get
|  set

Semantics

Name[Identifier  Identifier] = Name[Identifier];
Name[Identifier  get] = “get”;
Name[Identifier  set] = “set”;

Qualified Identifiers

Syntax

SimpleQualifiedIdentifier 
ExpressionQualifiedIdentifier  ParenExpression :: Identifier
QualifiedIdentifier 

Validation

proc Validate[SimpleQualifiedIdentifier] (cxtContextenvEnvironment)
[SimpleQualifiedIdentifier  Identifierdo
OpenNamespaces[SimpleQualifiedIdentifier cxt.openNamespaces;
Strict[SimpleQualifiedIdentifier cxt.strict;
[SimpleQualifiedIdentifier  Identifier :: Identifierdo
OpenNamespaces[SimpleQualifiedIdentifier cxt.openNamespaces;
[SimpleQualifiedIdentifier  ReservedNamespace :: Identifierdo
Evaluate Validate[ReservedNamespace](cxtenv) and ignore its result
end proc;
proc Validate[ExpressionQualifiedIdentifier  ParenExpression :: Identifier] (cxtContextenvEnvironment)
Strict[ExpressionQualifiedIdentifier cxt.strict;
Evaluate Validate[ParenExpression](cxtenv) and ignore its result
end proc;
proc Validate[QualifiedIdentifier] (cxtContextenvEnvironment)
[QualifiedIdentifier  SimpleQualifiedIdentifierdo
Strict[QualifiedIdentifier cxt.strict;
Evaluate Validate[SimpleQualifiedIdentifier](cxtenv) and ignore its result;
[QualifiedIdentifier  ExpressionQualifiedIdentifierdo
Strict[QualifiedIdentifier cxt.strict;
Evaluate Validate[ExpressionQualifiedIdentifier](cxtenv) and ignore its result
end proc;

Setup

proc Setup[SimpleQualifiedIdentifier] ()
[SimpleQualifiedIdentifier  Identifierdo nothing;
[SimpleQualifiedIdentifier  Identifier :: Identifierdo nothing;
[SimpleQualifiedIdentifier  ReservedNamespace :: Identifierdo
Evaluate Setup[ReservedNamespace]() and ignore its result
end proc;
proc Setup[ExpressionQualifiedIdentifier  ParenExpression :: Identifier] ()
Evaluate Setup[ParenExpression]() and ignore its result
end proc;
Setup[QualifiedIdentifier] () propagates the call to Setup to nonterminals in the expansion of QualifiedIdentifier.

Evaluation

proc Eval[SimpleQualifiedIdentifier] (envEnvironmentphasePhase): Multiname
[SimpleQualifiedIdentifier  Identifierdo
return {ns::(Name[Identifier]) | ns  OpenNamespaces[SimpleQualifiedIdentifier]};
[SimpleQualifiedIdentifier  Identifier1 :: Identifier2do
multinameMultiname  {ns::(Name[Identifier1]) | ns  OpenNamespaces[SimpleQualifiedIdentifier]};
aObject  lexicalRead(envmultinamephase);
if a  Namespace then
throw a TypeError exception — the qualifier must be a namespace
end if;
return {a::(Name[Identifier2])};
[SimpleQualifiedIdentifier  ReservedNamespace :: Identifierdo
qNamespace  Eval[ReservedNamespace](envphase);
return {q::(Name[Identifier])}
end proc;
proc Eval[ExpressionQualifiedIdentifier  ParenExpression :: Identifier] (envEnvironmentphasePhase): Multiname
qObject  readReference(Eval[ParenExpression](envphase), phase);
if q  Namespace then throw a TypeError exception — the qualifier must be a namespace
end if;
return {q::(Name[Identifier])}
end proc;
Eval[QualifiedIdentifier] (envEnvironmentphasePhase): Multiname propagates the call to Eval to nonterminals in the expansion of QualifiedIdentifier.

Primary Expressions

Syntax

PrimaryExpression 
   null
|  true
|  false
|  Number
|  String
|  this
|  RegularExpression
ReservedNamespace 
   public
|  private
ParenExpression  ( AssignmentExpressionallowIn )
ParenListExpression 
|  ( ListExpressionallowIn , AssignmentExpressionallowIn )

Validation

proc Validate[PrimaryExpression] (cxtContextenvEnvironment)
[PrimaryExpression  nulldo nothing;
[PrimaryExpression  truedo nothing;
[PrimaryExpression  falsedo nothing;
[PrimaryExpression  Numberdo nothing;
[PrimaryExpression  Stringdo nothing;
[PrimaryExpression  thisdo
frameParameterFrameOpt  getEnclosingParameterFrame(env);
if frame = none then
if cxt.strict then
throw a SyntaxError exception — this can be used outside a function only in non-strict mode
end if
elsif frame.kind = plainFunction then
throw a SyntaxError exception — this function does not define this
end if;
[PrimaryExpression  RegularExpressiondo nothing;
[PrimaryExpression  ReservedNamespacedo
Evaluate Validate[ReservedNamespace](cxtenv) and ignore its result;
[PrimaryExpression  ParenListExpressiondo
Evaluate Validate[ParenListExpression](cxtenv) and ignore its result;
[PrimaryExpression  ArrayLiteraldo
Evaluate Validate[ArrayLiteral](cxtenv) and ignore its result;
[PrimaryExpression  ObjectLiteraldo
Evaluate Validate[ObjectLiteral](cxtenv) and ignore its result;
[PrimaryExpression  FunctionExpressiondo
Evaluate Validate[FunctionExpression](cxtenv) and ignore its result
end proc;
proc Validate[ReservedNamespace] (cxtContextenvEnvironment)
[ReservedNamespace  publicdo nothing;
[ReservedNamespace  privatedo
if getEnclosingClass(env) = none then
throw a SyntaxError exception — private is meaningful only inside a class
end if
end proc;
Validate[ParenExpression] (cxtContextenvEnvironment) propagates the call to Validate to nonterminals in the expansion of ParenExpression.
Validate[ParenListExpression] (cxtContextenvEnvironment) propagates the call to Validate to nonterminals in the expansion of ParenListExpression.

Setup

Setup[PrimaryExpression] () propagates the call to Setup to nonterminals in the expansion of PrimaryExpression.
proc Setup[ReservedNamespace] ()
[ReservedNamespace  publicdo nothing;
[ReservedNamespace  privatedo nothing
end proc;
Setup[ParenExpression] () propagates the call to Setup to nonterminals in the expansion of ParenExpression.
Setup[ParenListExpression] () propagates the call to Setup to nonterminals in the expansion of ParenListExpression.

Evaluation

proc Eval[PrimaryExpression] (envEnvironmentphasePhase): ObjOrRef
[PrimaryExpression  nulldo return null;
[PrimaryExpression  truedo return true;
[PrimaryExpression  falsedo return false;
[PrimaryExpression  Numberdo return Value[Number];
[PrimaryExpression  Stringdo return Value[String];
[PrimaryExpression  thisdo
frameParameterFrameOpt  getEnclosingParameterFrame(env);
if frame = none then return getPackageFrame(envend if;
note  Validate ensured that frame.kind  plainFunction at this point.
thisObjectOpt  frame.this;
if this = none then
note  If Validate passed, this can be uninitialised only when phase = compile.
throw a ConstantError exception — a constant expression cannot read an uninitialised this parameter
end if;
if not frame.superconstructorCalled then
throw an UninitializedError exception — can’t access this from within a constructor before the superconstructor has been called
end if;
return this;
[PrimaryExpression  RegularExpressiondo
return Body[RegularExpression “#”  Flags[RegularExpression];
[PrimaryExpression  ReservedNamespacedo
return Eval[ReservedNamespace](envphase);
[PrimaryExpression  ParenListExpressiondo
return Eval[ParenListExpression](envphase);
[PrimaryExpression  ArrayLiteraldo return Eval[ArrayLiteral](envphase);
[PrimaryExpression  ObjectLiteraldo return Eval[ObjectLiteral](envphase);
[PrimaryExpression  FunctionExpressiondo
return Eval[FunctionExpression](envphase)
end proc;
proc Eval[ReservedNamespace] (envEnvironmentphasePhase): Namespace
[ReservedNamespace  publicdo return public;
[ReservedNamespace  privatedo
cClassOpt  getEnclosingClass(env);
note  Validate already ensured that c  none.
end proc;
Eval[ParenExpression] (envEnvironmentphasePhase): ObjOrRef propagates the call to Eval to nonterminals in the expansion of ParenExpression.
proc Eval[ParenListExpression] (envEnvironmentphasePhase): ObjOrRef
[ParenListExpression  ParenExpressiondo return Eval[ParenExpression](envphase);
[ParenListExpression  ( ListExpressionallowIn , AssignmentExpressionallowIn )do
Evaluate readReference(Eval[ListExpressionallowIn](envphase), phase) and ignore its result;
return readReference(Eval[AssignmentExpressionallowIn](envphase), phase)
end proc;
proc EvalAsList[ParenListExpression] (envEnvironmentphasePhase): Object[]
[ParenListExpression  ParenExpressiondo
eltObject  readReference(Eval[ParenExpression](envphase), phase);
return [elt];
[ParenListExpression  ( ListExpressionallowIn , AssignmentExpressionallowIn )do
eltsObject[]  EvalAsList[ListExpressionallowIn](envphase);
eltObject  readReference(Eval[AssignmentExpressionallowIn](envphase), phase);
return elts  [elt]
end proc;

Function Expressions

Syntax

FunctionExpression 
   function FunctionCommon
|  function Identifier FunctionCommon

Validation

proc Validate[FunctionExpression] (cxtContextenvEnvironment)
[FunctionExpression  function FunctionCommondo
kindStaticFunctionKind  plainFunction;
if not cxt.strict and Plain[FunctionCommonthen kind  uncheckedFunction end if;
F[FunctionExpression ValidateStaticFunction[FunctionCommon](cxtenvkind);
[FunctionExpression  function Identifier FunctionCommondo
vVariable  new VariabletypeFunction, valuenone, immutabletrue, setupnone, initializerbusy;
bLocalBinding  LocalBindingqnamepublic::(Name[Identifier]), accessesreadWrite, explicitfalse, enumerabletrue, contentv;
compileFrameLocalFrame  new LocalFramelocalBindings: {b};
kindStaticFunctionKind  plainFunction;
if not cxt.strict and Plain[FunctionCommonthen kind  uncheckedFunction end if;
F[FunctionExpression ValidateStaticFunction[FunctionCommon](cxt[compileFrame]  envkind)
end proc;

Setup

proc Setup[FunctionExpression] ()
[FunctionExpression  function FunctionCommondo
Evaluate Setup[FunctionCommon]() and ignore its result;
[FunctionExpression  function Identifier FunctionCommondo
Evaluate Setup[FunctionCommon]() and ignore its result
end proc;

Evaluation

proc Eval[FunctionExpression] (envEnvironmentphasePhase): ObjOrRef
[FunctionExpression  function FunctionCommondo
if phase = compile then
throw a ConstantError exception — a function expression is not a constant expression because it can evaluate to different values
end if;
[FunctionExpression  function Identifier FunctionCommondo
if phase = compile then
throw a ConstantError exception — a function expression is not a constant expression because it can evaluate to different values
end if;
vVariable  new VariabletypeFunction, valuenone, immutabletrue, setupnone, initializernone;
bLocalBinding  LocalBindingqnamepublic::(Name[Identifier]), accessesreadWrite, explicitfalse, enumerabletrue, contentv;
runtimeFrameLocalFrame  new LocalFramelocalBindings: {b};
fSimpleInstance  instantiateFunction(F[FunctionExpression], [runtimeFrame]  env);
v.value  f;
return f
end proc;

Object Literals

Syntax

ObjectLiteral  { FieldList }
FieldList 
   «empty»
NonemptyFieldList 
LiteralField  FieldName : AssignmentExpressionallowIn
FieldName 
|  String
|  Number

Validation

Validate[ObjectLiteral] (cxtContextenvEnvironment) propagates the call to Validate to nonterminals in the expansion of ObjectLiteral.
Validate[FieldList] (cxtContextenvEnvironment) propagates the call to Validate to nonterminals in the expansion of FieldList.
Validate[NonemptyFieldList] (cxtContextenvEnvironment) propagates the call to Validate to nonterminals in the expansion of NonemptyFieldList.
Validate[LiteralField] (cxtContextenvEnvironment) propagates the call to Validate to nonterminals in the expansion of LiteralField.
Validate[FieldName] (cxtContextenvEnvironment) propagates the call to Validate to nonterminals in the expansion of FieldName.

Setup

Setup[ObjectLiteral] () propagates the call to Setup to nonterminals in the expansion of ObjectLiteral.
Setup[FieldList] () propagates the call to Setup to nonterminals in the expansion of FieldList.
Setup[NonemptyFieldList] () propagates the call to Setup to nonterminals in the expansion of NonemptyFieldList.
Setup[LiteralField] () propagates the call to Setup to nonterminals in the expansion of LiteralField.
Setup[FieldName] () propagates the call to Setup to nonterminals in the expansion of FieldName.

Evaluation

proc Eval[ObjectLiteral  { FieldList }] (envEnvironmentphasePhase): ObjOrRef
if phase = compile then
throw a ConstantError exception — an object literal is not a constant expression because it evaluates to a new object each time it is evaluated
end if;
oObject  construct(Object[]phase);
Evaluate Eval[FieldList](envophase) and ignore its result;
return o
end proc;
Eval[FieldList] (envEnvironmentoObjectphase: {run}) propagates the call to Eval to nonterminals in the expansion of FieldList.
Eval[NonemptyFieldList] (envEnvironmentoObjectphase: {run}) propagates the call to Eval to nonterminals in the expansion of NonemptyFieldList.
proc Eval[LiteralField  FieldName : AssignmentExpressionallowIn] (envEnvironmentoObjectphase: {run})
multinameMultiname  Eval[FieldName](envphase);
valueObject  readReference(Eval[AssignmentExpressionallowIn](envphase), phase);
Evaluate dotWrite(omultinamevaluephase) and ignore its result
end proc;
proc Eval[FieldName] (envEnvironmentphasePhase): Multiname
[FieldName  QualifiedIdentifierdo return Eval[QualifiedIdentifier](envphase);
[FieldName  Stringdo return {objectToQualifiedName(Value[String], phase)};
[FieldName  Numberdo return {objectToQualifiedName(Value[Number], phase)};
[FieldName  ParenExpressiondo
aObject  readReference(Eval[ParenExpression](envphase), phase);
return {objectToQualifiedName(aphase)}
end proc;

Array Literals

Syntax

ArrayLiteral  [ ElementList ]
ElementList 
   «empty»
|  , ElementList
LiteralElement  AssignmentExpressionallowIn

Validation

Validate[ArrayLiteral] (cxtContextenvEnvironment) propagates the call to Validate to nonterminals in the expansion of ArrayLiteral.
Validate[ElementList] (cxtContextenvEnvironment) propagates the call to Validate to nonterminals in the expansion of ElementList.
Validate[LiteralElement] (cxtContextenvEnvironment) propagates the call to Validate to nonterminals in the expansion of LiteralElement.

Setup

Setup[ArrayLiteral] () propagates the call to Setup to nonterminals in the expansion of ArrayLiteral.
Setup[ElementList] () propagates the call to Setup to nonterminals in the expansion of ElementList.
Setup[LiteralElement] () propagates the call to Setup to nonterminals in the expansion of LiteralElement.

Evaluation

proc Eval[ArrayLiteral  [ ElementList ]] (envEnvironmentphasePhase): ObjOrRef
if phase = compile then
throw a ConstantError exception — an array literal is not a constant expression because it evaluates to a new object each time it is evaluated
end if;
oObject  construct(Array[]phase);
lengthInteger  Eval[ElementList](env, 0, ophase);
Evaluate writeArrayPrivateLength(olengthphase) and ignore its result;
return o
end proc;
proc Eval[ElementList] (envEnvironmentlengthIntegeroObjectphase: {run}): Integer
[ElementList  «empty»] do return length;
[ElementList  LiteralElementdo
Evaluate Eval[LiteralElement](envlengthophase) and ignore its result;
return length + 1;
[ElementList0  , ElementList1do
return Eval[ElementList1](envlength + 1, ophase);
[ElementList0  LiteralElement , ElementList1do
Evaluate Eval[LiteralElement](envlengthophase) and ignore its result;
return Eval[ElementList1](envlength + 1, ophase)
end proc;
proc Eval[LiteralElement  AssignmentExpressionallowIn] (envEnvironmentlengthIntegeroObjectphase: {run})
valueObject  readReference(Eval[AssignmentExpressionallowIn](envphase), phase);
Evaluate indexWrite(olengthvaluephase) and ignore its result
end proc;

Super Expressions

Syntax

SuperExpression 
   super
|  super ParenExpression

Validation

proc Validate[SuperExpression] (cxtContextenvEnvironment)
[SuperExpression  superdo
cClassOpt  getEnclosingClass(env);
if c = none then
throw a SyntaxError exception — a super expression is meaningful only inside a class
end if;
frameParameterFrameOpt  getEnclosingParameterFrame(env);
if frame = none or frame.kind  StaticFunctionKind then
throw a SyntaxError exception — a super expression without an argument is meaningful only inside an instance method or a constructor
end if;
if c.super = none then
throw a SyntaxError exception — a super expression is meaningful only if the enclosing class has a superclass
end if;
[SuperExpression  super ParenExpressiondo
cClassOpt  getEnclosingClass(env);
if c = none then
throw a SyntaxError exception — a super expression is meaningful only inside a class
end if;
if c.super = none then
throw a SyntaxError exception — a super expression is meaningful only if the enclosing class has a superclass
end if;
Evaluate Validate[ParenExpression](cxtenv) and ignore its result
end proc;

Setup

Setup[SuperExpression] () propagates the call to Setup to nonterminals in the expansion of SuperExpression.

Evaluation

proc Eval[SuperExpression] (envEnvironmentphasePhase): ObjOptionalLimit
[SuperExpression  superdo
frameParameterFrameOpt  getEnclosingParameterFrame(env);
note  Validate ensured that frame  none and frame.kind  StaticFunctionKind at this point.
thisObjectOpt  frame.this;
if this = none then
note  If Validate passed, this can be uninitialised only when phase = compile.
throw a ConstantError exception — a constant expression cannot read an uninitialised this parameter
end if;
if not frame.superconstructorCalled then
throw an UninitializedError exception — can’t access super from within a constructor before the superconstructor has been called
end if;
return makeLimitedInstance(thisgetEnclosingClass(env), phase);
[SuperExpression  super ParenExpressiondo
rObjOrRef  Eval[ParenExpression](envphase);
return makeLimitedInstance(rgetEnclosingClass(env), phase)
end proc;
proc makeLimitedInstance(rObjOrRefcClassphasePhase): ObjOptionalLimit
oObject  readReference(rphase);
limitClassOpt  c.super;
note  Validate ensured that limit cannot be none at this point.
coercedObject  coerce(olimit);
if coerced = null then return null end if;
return LimitedInstanceinstancecoercedlimitlimit
end proc;

Postfix Expressions

Syntax

PostfixExpression 
AttributeExpression 
FullPostfixExpression 
|  PostfixExpression [no line break] ++
|  PostfixExpression [no line break] --
FullNewExpression  new FullNewSubexpression Arguments
FullNewSubexpression 
ShortNewExpression  new ShortNewSubexpression
ShortNewSubexpression 

Validation

Validate[PostfixExpression] (cxtContextenvEnvironment) propagates the call to Validate to nonterminals in the expansion of PostfixExpression.
Validate[AttributeExpression] (cxtContextenvEnvironment) propagates the call to Validate to nonterminals in the expansion of AttributeExpression.
Validate[FullPostfixExpression] (cxtContextenvEnvironment) propagates the call to Validate to nonterminals in the expansion of FullPostfixExpression.
Validate[FullNewExpression] (cxtContextenvEnvironment) propagates the call to Validate to nonterminals in the expansion of FullNewExpression.
Validate[FullNewSubexpression] (cxtContextenvEnvironment) propagates the call to Validate to nonterminals in the expansion of FullNewSubexpression.
Validate[ShortNewExpression] (cxtContextenvEnvironment) propagates the call to Validate to nonterminals in the expansion of ShortNewExpression.
Validate[ShortNewSubexpression] (cxtContextenvEnvironment) propagates the call to Validate to nonterminals in the expansion of ShortNewSubexpression.

Setup

Setup[PostfixExpression] () propagates the call to Setup to nonterminals in the expansion of PostfixExpression.
Setup[AttributeExpression] () propagates the call to Setup to nonterminals in the expansion of AttributeExpression.
Setup[FullPostfixExpression] () propagates the call to Setup to nonterminals in the expansion of FullPostfixExpression.
Setup[FullNewExpression] () propagates the call to Setup to nonterminals in the expansion of FullNewExpression.
Setup[FullNewSubexpression] () propagates the call to Setup to nonterminals in the expansion of FullNewSubexpression.
Setup[ShortNewExpression] () propagates the call to Setup to nonterminals in the expansion of ShortNewExpression.
Setup[ShortNewSubexpression] () propagates the call to Setup to nonterminals in the expansion of ShortNewSubexpression.

Evaluation

Eval[PostfixExpression] (envEnvironmentphasePhase): ObjOrRef propagates the call to Eval to nonterminals in the expansion of PostfixExpression.
proc Eval[AttributeExpression] (envEnvironmentphasePhase): ObjOrRef
[AttributeExpression  SimpleQualifiedIdentifierdo
mMultiname  Eval[SimpleQualifiedIdentifier](envphase);
return LexicalReferenceenvenv, variableMultinamem, strictStrict[SimpleQualifiedIdentifier];
[AttributeExpression0  AttributeExpression1 PropertyOperatordo
aObject  readReference(Eval[AttributeExpression1](envphase), phase);
return Eval[PropertyOperator](envaphase);
[AttributeExpression0  AttributeExpression1 Argumentsdo
rObjOrRef  Eval[AttributeExpression1](envphase);
fObject  readReference(rphase);
baseObject;
case r of
Object  LexicalReference do base  null;
DotReference  BracketReference do base  r.base
end case;
argsObject[]  Eval[Arguments](envphase);
return call(basefargsphase)
end proc;
proc Eval[FullPostfixExpression] (envEnvironmentphasePhase): ObjOrRef
[FullPostfixExpression  PrimaryExpressiondo
return Eval[PrimaryExpression](envphase);
mMultiname  Eval[ExpressionQualifiedIdentifier](envphase);
return LexicalReferenceenvenv, variableMultinamem, strictStrict[ExpressionQualifiedIdentifier];
[FullPostfixExpression  FullNewExpressiondo
return Eval[FullNewExpression](envphase);
aObject  readReference(Eval[FullPostfixExpression1](envphase), phase);
return Eval[PropertyOperator](envaphase);
aObjOptionalLimit  Eval[SuperExpression](envphase);
return Eval[PropertyOperator](envaphase);
[FullPostfixExpression0  FullPostfixExpression1 Argumentsdo
rObjOrRef  Eval[FullPostfixExpression1](envphase);
fObject  readReference(rphase);
baseObject;
case r of
Object  LexicalReference do base  null;
DotReference  BracketReference do base  r.base
end case;
argsObject[]  Eval[Arguments](envphase);
return call(basefargsphase);
[FullPostfixExpression  PostfixExpression [no line break] ++do
if phase = compile then
throw a ConstantError exception — ++ cannot be used in a constant expression
end if;
rObjOrRef  Eval[PostfixExpression](envphase);
aObject  readReference(rphase);
bObject  plus(aphase);
cObject  add(b, 1f64phase);
Evaluate writeReference(rcphase) and ignore its result;
return b;
[FullPostfixExpression  PostfixExpression [no line break] --do
if phase = compile then
throw a ConstantError exception — -- cannot be used in a constant expression
end if;
rObjOrRef  Eval[PostfixExpression](envphase);
aObject  readReference(rphase);
bObject  plus(aphase);
cObject  subtract(b, 1f64phase);
Evaluate writeReference(rcphase) and ignore its result;
return b
end proc;
proc Eval[FullNewExpression  new FullNewSubexpression Arguments] (envEnvironmentphasePhase): ObjOrRef
fObject  readReference(Eval[FullNewSubexpression](envphase), phase);
argsObject[]  Eval[Arguments](envphase);
return construct(fargsphase)
end proc;
proc Eval[FullNewSubexpression] (envEnvironmentphasePhase): ObjOrRef
[FullNewSubexpression  PrimaryExpressiondo
return Eval[PrimaryExpression](envphase);
[FullNewSubexpression  QualifiedIdentifierdo
mMultiname  Eval[QualifiedIdentifier](envphase);
return LexicalReferenceenvenv, variableMultinamem, strictStrict[QualifiedIdentifier];
[FullNewSubexpression  FullNewExpressiondo
return Eval[FullNewExpression](envphase);
aObject  readReference(Eval[FullNewSubexpression1](envphase), phase);
return Eval[PropertyOperator](envaphase);
[FullNewSubexpression  SuperExpression PropertyOperatordo
aObjOptionalLimit  Eval[SuperExpression](envphase);
return Eval[PropertyOperator](envaphase)
end proc;
proc Eval[ShortNewExpression  new ShortNewSubexpression] (envEnvironmentphasePhase): ObjOrRef
fObject  readReference(Eval[ShortNewSubexpression](envphase), phase);
return construct(f[]phase)
end proc;
Eval[ShortNewSubexpression] (envEnvironmentphasePhase): ObjOrRef propagates the call to Eval to nonterminals in the expansion of ShortNewSubexpression.

Property Operators

Syntax

PropertyOperator 
Brackets 
   [ ]
|  [ ListExpressionallowIn ]
Arguments 
   ( )
ExpressionsWithRest 
RestExpression  ... AssignmentExpressionallowIn

Validation

Validate[PropertyOperator] (cxtContextenvEnvironment) propagates the call to Validate to nonterminals in the expansion of PropertyOperator.
Validate[Brackets] (cxtContextenvEnvironment) propagates the call to Validate to nonterminals in the expansion of Brackets.
Validate[Arguments] (cxtContextenvEnvironment) propagates the call to Validate to nonterminals in the expansion of Arguments.
Validate[ExpressionsWithRest] (cxtContextenvEnvironment) propagates the call to Validate to nonterminals in the expansion of ExpressionsWithRest.
Validate[RestExpression] (cxtContextenvEnvironment) propagates the call to Validate to nonterminals in the expansion of RestExpression.

Setup

Setup[PropertyOperator] () propagates the call to Setup to nonterminals in the expansion of PropertyOperator.
Setup[Brackets] () propagates the call to Setup to nonterminals in the expansion of Brackets.
Setup[Arguments] () propagates the call to Setup to nonterminals in the expansion of Arguments.
Setup[ExpressionsWithRest] () propagates the call to Setup to nonterminals in the expansion of ExpressionsWithRest.
Setup[RestExpression] () propagates the call to Setup to nonterminals in the expansion of RestExpression.

Evaluation

proc Eval[PropertyOperator] (envEnvironmentbaseObjOptionalLimitphasePhase): ObjOrRef
[PropertyOperator  . QualifiedIdentifierdo
mMultiname  Eval[QualifiedIdentifier](envphase);
case base of
Object do
return DotReferencebasebaselimitobjectType(base), multinamem;
return DotReferencebasebase.instancelimitbase.limitmultinamem
end case;
[PropertyOperator  Bracketsdo
argsObject[]  Eval[Brackets](envphase);
case base of
Object do
return BracketReferencebasebaselimitobjectType(base), argsargs;
return BracketReferencebasebase.instancelimitbase.limitargsargs
end case
end proc;
proc Eval[Brackets] (envEnvironmentphasePhase): Object[]
[Brackets  [ ]do return [];
[Brackets  [ ListExpressionallowIn ]do
return EvalAsList[ListExpressionallowIn](envphase);
[Brackets  [ ExpressionsWithRest ]do return Eval[ExpressionsWithRest](envphase)
end proc;
proc Eval[Arguments] (envEnvironmentphasePhase): Object[]
[Arguments  ( )do return [];
[Arguments  ParenListExpressiondo
return EvalAsList[ParenListExpression](envphase);
[Arguments  ( ExpressionsWithRest )do
return Eval[ExpressionsWithRest](envphase)
end proc;
proc Eval[ExpressionsWithRest] (envEnvironmentphasePhase): Object[]
[ExpressionsWithRest  RestExpressiondo return Eval[RestExpression](envphase);
[ExpressionsWithRest  ListExpressionallowIn , RestExpressiondo
args1Object[]  EvalAsList[ListExpressionallowIn](envphase);
args2Object[]  Eval[RestExpression](envphase);
return args1  args2
end proc;
proc Eval[RestExpression  ... AssignmentExpressionallowIn] (envEnvironmentphasePhase): Object[]
aObject  readReference(Eval[AssignmentExpressionallowIn](envphase), phase);
lengthInteger  readLength(aphase);
iInteger  0;
argsObject[]  [];
while i  length do
argObjectOpt  indexRead(aiphase);
if arg = none then
An implementation may, at its discretion, either throw a ReferenceError or treat the hole as a missing argument, substituting the called function’s default parameter value if there is one, undefined if the called function is unchecked, or throwing an ArgumentError exception otherwise. An implementation must not replace such a hole with undefined except when the called function is unchecked or happens to have undefined as its default parameter value.
end if;
args  args  [arg];
i  i + 1
end while;
return args
end proc;

Unary Operators

Syntax

UnaryExpression 
|  delete PostfixExpression
|  void UnaryExpression
|  typeof UnaryExpression
|  - NegatedMinLong

Validation

proc Validate[UnaryExpression] (cxtContextenvEnvironment)
[UnaryExpression  PostfixExpressiondo
Evaluate Validate[PostfixExpression](cxtenv) and ignore its result;
[UnaryExpression  delete PostfixExpressiondo
Evaluate Validate[PostfixExpression](cxtenv) and ignore its result;
Strict[UnaryExpression cxt.strict;
[UnaryExpression0  void UnaryExpression1do
Evaluate Validate[UnaryExpression1](cxtenv) and ignore its result;
[UnaryExpression0  typeof UnaryExpression1do
Evaluate Validate[UnaryExpression1](cxtenv) and ignore its result;
[UnaryExpression  ++ PostfixExpressiondo
Evaluate Validate[PostfixExpression](cxtenv) and ignore its result;
[UnaryExpression  -- PostfixExpressiondo
Evaluate Validate[PostfixExpression](cxtenv) and ignore its result;
[UnaryExpression0  + UnaryExpression1do
Evaluate Validate[UnaryExpression1](cxtenv) and ignore its result;
[UnaryExpression0  - UnaryExpression1do
Evaluate Validate[UnaryExpression1](cxtenv) and ignore its result;
[UnaryExpression  - NegatedMinLongdo nothing;
[UnaryExpression0  ~ UnaryExpression1do
Evaluate Validate[UnaryExpression1](cxtenv) and ignore its result;
[UnaryExpression0  ! UnaryExpression1do
Evaluate Validate[UnaryExpression1](cxtenv) and ignore its result
end proc;

Setup

Setup[UnaryExpression] () propagates the call to Setup to nonterminals in the expansion of UnaryExpression.

Evaluation

proc Eval[UnaryExpression] (envEnvironmentphasePhase): ObjOrRef
[UnaryExpression  PostfixExpressiondo return Eval[PostfixExpression](envphase);
[UnaryExpression  delete PostfixExpressiondo
if phase = compile then
throw a ConstantError exception — delete cannot be used in a constant expression
end if;
rObjOrRef  Eval[PostfixExpression](envphase);
return deleteReference(rStrict[UnaryExpression], phase);
[UnaryExpression0  void UnaryExpression1do
Evaluate readReference(Eval[UnaryExpression1](envphase), phase) and ignore its result;
return undefined;
[UnaryExpression0  typeof UnaryExpression1do
aObject  readReference(Eval[UnaryExpression1](envphase), phase);
cClass  objectType(a);
return c.typeofString;
[UnaryExpression  ++ PostfixExpressiondo
if phase = compile then
throw a ConstantError exception — ++ cannot be used in a constant expression
end if;
rObjOrRef  Eval[PostfixExpression](envphase);
aObject  readReference(rphase);
bObject  plus(aphase);
cObject  add(b, 1f64phase);
Evaluate writeReference(rcphase) and ignore its result;
return c;
[UnaryExpression  -- PostfixExpressiondo
if phase = compile then
throw a ConstantError exception — -- cannot be used in a constant expression
end if;
rObjOrRef  Eval[PostfixExpression](envphase);
aObject  readReference(rphase);
bObject  plus(aphase);
cObject  subtract(b, 1f64phase);
Evaluate writeReference(rcphase) and ignore its result;
return c;
[UnaryExpression0  + UnaryExpression1do
aObject  readReference(Eval[UnaryExpression1](envphase), phase);
return plus(aphase);
[UnaryExpression0  - UnaryExpression1do
aObject  readReference(Eval[UnaryExpression1](envphase), phase);
return minus(aphase);
[UnaryExpression  - NegatedMinLongdo return (–263)long;
[UnaryExpression0  ~ UnaryExpression1do
aObject  readReference(Eval[UnaryExpression1](envphase), phase);
return bitNot(aphase);
[UnaryExpression0  ! UnaryExpression1do
aObject  readReference(Eval[UnaryExpression1](envphase), phase);
return logicalNot(aphase)
end proc;
plus(aphase) returns the value of the unary expression +a. If phase is compile, only constant operations are permitted.
proc plus(aObjectphasePhase): Object
return objectToGeneralNumber(aphase)
end proc;
minus(aphase) returns the value of the unary expression -a. If phase is compile, only constant operations are permitted.
proc minus(aObjectphasePhase): Object
xGeneralNumber  objectToGeneralNumber(aphase);
end proc;
proc generalNumberNegate(xGeneralNumber): GeneralNumber
case x of
Long do return integerToLong(–x.value);
ULong do return integerToULong(–x.value);
Float32 do return float32Negate(x);
Float64 do return float64Negate(x)
end case
end proc;
proc bitNot(aObjectphasePhase): Object
xGeneralNumber  objectToGeneralNumber(aphase);
case x of
Long do i: {–263 ... 263 – 1}  x.valuereturn (bitwiseXor(i, –1))long;
ULong do
i: {0 ... 264 – 1}  x.value;
return (bitwiseXor(i, 0xFFFFFFFFFFFFFFFF))ulong;
Float32  Float64 do
i: {–231 ... 231 – 1}  signedWrap32(truncateToInteger(x));
return (bitwiseXor(i, –1))f64
end case
end proc;
logicalNot(aphase) returns the value of the unary expression !a. If phase is compile, only constant operations are permitted.
proc logicalNot(aObjectphasePhase): Object
return not objectToBoolean(a)
end proc;

Multiplicative Operators

Syntax

MultiplicativeExpression 

Validation

Validate[MultiplicativeExpression] (cxtContextenvEnvironment) propagates the call to Validate to nonterminals in the expansion of MultiplicativeExpression.

Setup

Setup[MultiplicativeExpression] () propagates the call to Setup to nonterminals in the expansion of MultiplicativeExpression.

Evaluation

proc Eval[MultiplicativeExpression] (envEnvironmentphasePhase): ObjOrRef
[MultiplicativeExpression  UnaryExpressiondo
return Eval[UnaryExpression](envphase);
aObject  readReference(Eval[MultiplicativeExpression1](envphase), phase);
bObject  readReference(Eval[UnaryExpression](envphase), phase);
return multiply(abphase);
aObject  readReference(Eval[MultiplicativeExpression1](envphase), phase);
bObject  readReference(Eval[UnaryExpression](envphase), phase);
return divide(abphase);
aObject  readReference(Eval[MultiplicativeExpression1](envphase), phase);
bObject  readReference(Eval[UnaryExpression](envphase), phase);
return remainder(abphase)
end proc;
proc multiply(aObjectbObjectphasePhase): Object
xGeneralNumber  objectToGeneralNumber(aphase);
yGeneralNumber  objectToGeneralNumber(bphase);
if x  Long  ULong or y  Long  ULong then
iIntegerOpt  checkInteger(x);
jIntegerOpt  checkInteger(y);
if i  none and j  none then
kInteger  ij;
if x  ULong or y  ULong then return integerToULong(k)
else return integerToLong(k)
end if
end if
end if;
end proc;
proc divide(aObjectbObjectphasePhase): Object
xGeneralNumber  objectToGeneralNumber(aphase);
yGeneralNumber  objectToGeneralNumber(bphase);
if x  Long  ULong or y  Long  ULong then
iIntegerOpt  checkInteger(x);
jIntegerOpt  checkInteger(y);
if i  none and j  none and j  0 then
qRational  i/j;
if x  ULong or y  ULong then return rationalToULong(q)
else return rationalToLong(q)
end if
end if
end if;
end proc;
proc remainder(aObjectbObjectphasePhase): Object
xGeneralNumber  objectToGeneralNumber(aphase);
yGeneralNumber  objectToGeneralNumber(bphase);
if x  Long  ULong or y  Long  ULong then
iIntegerOpt  checkInteger(x);
jIntegerOpt  checkInteger(y);
if i  none and j  none and j  0 then
qRational  i/j;
kInteger  q  0 ? q : q;
rInteger  i – jk;
if x  ULong or y  ULong then return integerToULong(r)
else return integerToLong(r)
end if
end if
end if;
end proc;

Additive Operators

Syntax

AdditiveExpression 

Validation

Validate[AdditiveExpression] (cxtContextenvEnvironment) propagates the call to Validate to nonterminals in the expansion of AdditiveExpression.

Setup

Setup[AdditiveExpression] () propagates the call to Setup to nonterminals in the expansion of AdditiveExpression.

Evaluation

proc Eval[AdditiveExpression] (envEnvironmentphasePhase): ObjOrRef
[AdditiveExpression  MultiplicativeExpressiondo
return Eval[MultiplicativeExpression](envphase);
aObject  readReference(Eval[AdditiveExpression1](envphase), phase);
bObject  readReference(Eval[MultiplicativeExpression](envphase), phase);
return add(abphase);
aObject  readReference(Eval[AdditiveExpression1](envphase), phase);
bObject  readReference(Eval[MultiplicativeExpression](envphase), phase);
return subtract(abphase)
end proc;
proc add(aObjectbObjectphasePhase): Object
apPrimitiveObject  objectToPrimitive(anonephase);
bpPrimitiveObject  objectToPrimitive(bnonephase);
if ap  Char16  String or bp  Char16  String then
return objectToString(apphase objectToString(bpphase)
end if;
xGeneralNumber  objectToGeneralNumber(apphase);
yGeneralNumber  objectToGeneralNumber(bpphase);
if x  Long  ULong or y  Long  ULong then
iIntegerOpt  checkInteger(x);
jIntegerOpt  checkInteger(y);
if i  none and j  none then
kInteger  i + j;
if x  ULong or y  ULong then return integerToULong(k)
else return integerToLong(k)
end if
end if
end if;
return float64Add(toFloat64(x), toFloat64(y))
end proc;
proc subtract(aObjectbObjectphasePhase): Object
xGeneralNumber  objectToGeneralNumber(aphase);
yGeneralNumber  objectToGeneralNumber(bphase);
if x  Long  ULong or y  Long  ULong then
iIntegerOpt  checkInteger(x);
jIntegerOpt  checkInteger(y);
if i  none and j  none then
kInteger  i – j;
if x  ULong or y  ULong then return integerToULong(k)
else return integerToLong(k)
end if
end if
end if;
end proc;

Bitwise Shift Operators

Syntax

ShiftExpression 

Validation

Validate[ShiftExpression] (cxtContextenvEnvironment) propagates the call to Validate to nonterminals in the expansion of ShiftExpression.

Setup

Setup[ShiftExpression] () propagates the call to Setup to nonterminals in the expansion of ShiftExpression.

Evaluation

proc Eval[ShiftExpression] (envEnvironmentphasePhase): ObjOrRef
[ShiftExpression  AdditiveExpressiondo
return Eval[AdditiveExpression](envphase);
[ShiftExpression0  ShiftExpression1 << AdditiveExpressiondo
aObject  readReference(Eval[ShiftExpression1](envphase), phase);
bObject  readReference(Eval[AdditiveExpression](envphase), phase);
return shiftLeft(abphase);
[ShiftExpression0  ShiftExpression1 >> AdditiveExpressiondo
aObject  readReference(Eval[ShiftExpression1](envphase), phase);
bObject  readReference(Eval[AdditiveExpression](envphase), phase);
return shiftRight(abphase);
[ShiftExpression0  ShiftExpression1 >>> AdditiveExpressiondo
aObject  readReference(Eval[ShiftExpression1](envphase), phase);
bObject  readReference(Eval[AdditiveExpression](envphase), phase);
return shiftRightUnsigned(abphase)
end proc;
proc shiftLeft(aObjectbObjectphasePhase): Object
xGeneralNumber  objectToGeneralNumber(aphase);
countInteger  truncateToInteger(objectToGeneralNumber(bphase));
case x of
Float32  Float64 do
count  bitwiseAnd(count, 0x1F);
i: {–231 ... 231 – 1}  signedWrap32(bitwiseShift(truncateToInteger(x), count));
return if64;
Long do
count  bitwiseAnd(count, 0x3F);
i: {–263 ... 263 – 1}  signedWrap64(bitwiseShift(x.valuecount));
return ilong;
ULong do
count  bitwiseAnd(count, 0x3F);
i: {0 ... 264 – 1}  unsignedWrap64(bitwiseShift(x.valuecount));
return iulong
end case
end proc;
proc shiftRight(aObjectbObjectphasePhase): Object
xGeneralNumber  objectToGeneralNumber(aphase);
countInteger  truncateToInteger(objectToGeneralNumber(bphase));
case x of
Float32  Float64 do
i: {–231 ... 231 – 1}  signedWrap32(truncateToInteger(x));
count  bitwiseAnd(count, 0x1F);
i  bitwiseShift(i, –count);
return if64;
Long do
count  bitwiseAnd(count, 0x3F);
i: {–263 ... 263 – 1}  bitwiseShift(x.value, –count);
return ilong;
ULong do
count  bitwiseAnd(count, 0x3F);
i: {–263 ... 263 – 1}  bitwiseShift(signedWrap64(x.value), –count);
return (unsignedWrap64(i))ulong
end case
end proc;
proc shiftRightUnsigned(aObjectbObjectphasePhase): Object
xGeneralNumber  objectToGeneralNumber(aphase);
countInteger  truncateToInteger(objectToGeneralNumber(bphase));
case x of
Float32  Float64 do
i: {0 ... 232 – 1}  unsignedWrap32(truncateToInteger(x));
count  bitwiseAnd(count, 0x1F);
i  bitwiseShift(i, –count);
return if64;
Long do
count  bitwiseAnd(count, 0x3F);
i: {0 ... 264 – 1}  bitwiseShift(unsignedWrap64(x.value), –count);
return (signedWrap64(i))long;
ULong do
count  bitwiseAnd(count, 0x3F);
i: {0 ... 264 – 1}  bitwiseShift(x.value, –count);
return iulong
end case
end proc;

Relational Operators

Syntax

RelationalExpressionallowIn 
|  RelationalExpressionallowIn instanceof ShiftExpression
RelationalExpressionnoIn 

Validation

Validate[RelationalExpression] (cxtContextenvEnvironment) propagates the call to Validate to nonterminals in the expansion of RelationalExpression.

Setup

Setup[RelationalExpression] () propagates the call to Setup to nonterminals in the expansion of RelationalExpression.

Evaluation

proc Eval[RelationalExpression] (envEnvironmentphasePhase): ObjOrRef
[RelationalExpression  ShiftExpressiondo
return Eval[ShiftExpression](envphase);
[RelationalExpression0  RelationalExpression1 < ShiftExpressiondo
aObject  readReference(Eval[RelationalExpression1](envphase), phase);
bObject  readReference(Eval[ShiftExpression](envphase), phase);
return isLess(abphase);
[RelationalExpression0  RelationalExpression1 > ShiftExpressiondo
aObject  readReference(Eval[RelationalExpression1](envphase), phase);
bObject  readReference(Eval[ShiftExpression](envphase), phase);
return isLess(baphase);
[RelationalExpression0  RelationalExpression1 <= ShiftExpressiondo
aObject  readReference(Eval[RelationalExpression1](envphase), phase);
bObject  readReference(Eval[ShiftExpression](envphase), phase);
return isLessOrEqual(abphase);
[RelationalExpression0  RelationalExpression1 >= ShiftExpressiondo
aObject  readReference(Eval[RelationalExpression1](envphase), phase);
bObject  readReference(Eval[ShiftExpression](envphase), phase);
return isLessOrEqual(baphase);
[RelationalExpression0  RelationalExpression1 is ShiftExpressiondo
aObject  readReference(Eval[RelationalExpression1](envphase), phase);
bObject  readReference(Eval[ShiftExpression](envphase), phase);
cClass  objectToClass(b);
return is(ac);
[RelationalExpression0  RelationalExpression1 as ShiftExpressiondo
aObject  readReference(Eval[RelationalExpression1](envphase), phase);
bObject  readReference(Eval[ShiftExpression](envphase), phase);
cClass  objectToClass(b);
return coerceOrNull(ac);
[RelationalExpressionallowIn0  RelationalExpressionallowIn1 in ShiftExpressiondo
aObject  readReference(Eval[RelationalExpressionallowIn1](envphase), phase);
bObject  readReference(Eval[ShiftExpression](envphase), phase);
return hasProperty(bafalsephase);
[RelationalExpression0  RelationalExpression1 instanceof ShiftExpressiondo
aObject  readReference(Eval[RelationalExpression1](envphase), phase);
bObject  readReference(Eval[ShiftExpression](envphase), phase);
if b  Class then return is(ab)
elsif is(bPrototypeFunctionthen
prototypeObject  dotRead(b, {public::“prototype”}, phase);
return prototype  archetypes(a)
else throw a TypeError exception
end if
end proc;
proc isLess(aObjectbObjectphasePhase): Boolean
apPrimitiveObject  objectToPrimitive(ahintNumberphase);
bpPrimitiveObject  objectToPrimitive(bhintNumberphase);
if ap  Char16  String and bp  Char16  String then
return toString(ap) < toString(bp)
end if;
end proc;
proc isLessOrEqual(aObjectbObjectphasePhase): Boolean
apPrimitiveObject  objectToPrimitive(ahintNumberphase);
bpPrimitiveObject  objectToPrimitive(bhintNumberphase);
if ap  Char16  String and bp  Char16  String then
return toString(ap toString(bp)
end if;
return generalNumberCompare(objectToGeneralNumber(apphase), objectToGeneralNumber(bpphase))  {lessequal}
end proc;

Equality Operators

Syntax

EqualityExpression 
   RelationalExpression
|  EqualityExpression == RelationalExpression
|  EqualityExpression != RelationalExpression
|  EqualityExpression === RelationalExpression
|  EqualityExpression !== RelationalExpression

Validation

Validate[EqualityExpression] (cxtContextenvEnvironment) propagates the call to Validate to nonterminals in the expansion of EqualityExpression.

Setup

Setup[EqualityExpression] () propagates the call to Setup to nonterminals in the expansion of EqualityExpression.

Evaluation

proc Eval[EqualityExpression] (envEnvironmentphasePhase): ObjOrRef
[EqualityExpression  RelationalExpressiondo
return Eval[RelationalExpression](envphase);
[EqualityExpression0  EqualityExpression1 == RelationalExpressiondo
aObject  readReference(Eval[EqualityExpression1](envphase), phase);
bObject  readReference(Eval[RelationalExpression](envphase), phase);
return isEqual(abphase);
[EqualityExpression0  EqualityExpression1 != RelationalExpressiondo
aObject  readReference(Eval[EqualityExpression1](envphase), phase);
bObject  readReference(Eval[RelationalExpression](envphase), phase);
return not isEqual(abphase);
[EqualityExpression0  EqualityExpression1 === RelationalExpressiondo
aObject  readReference(Eval[EqualityExpression1](envphase), phase);
bObject  readReference(Eval[RelationalExpression](envphase), phase);
return isStrictlyEqual(abphase);
[EqualityExpression0  EqualityExpression1 !== RelationalExpressiondo
aObject  readReference(Eval[EqualityExpression1](envphase), phase);
bObject  readReference(Eval[RelationalExpression](envphase), phase);
return not isStrictlyEqual(abphase)
end proc;
proc isEqual(aObjectbObjectphasePhase): Boolean
case a of
Undefined  Null do return b  Undefined  Null;
if b  Boolean then return a = b
else return isEqual(objectToGeneralNumber(aphase), bphase)
end if;
bpPrimitiveObject  objectToPrimitive(bnonephase);
case bp of
Undefined  Null do return false;
Boolean  GeneralNumber  Char16  String do
end case;
Char16  String do
bpPrimitiveObject  objectToPrimitive(bnonephase);
case bp of
Undefined  Null do return false;
Boolean  GeneralNumber do
Char16  String do return toString(a) = toString(bp)
end case;
Namespace  CompoundAttribute  Class  MethodClosure  SimpleInstance  Date  RegExp  Package do
case b of
Undefined  Null do return false;
Namespace  CompoundAttribute  Class  MethodClosure  SimpleInstance  Date  RegExp  Package do
return isStrictlyEqual(abphase);
Boolean  GeneralNumber  Char16  String do
apPrimitiveObject  objectToPrimitive(anonephase);
return isEqual(apbphase)
end case
end case
end proc;
proc isStrictlyEqual(aObjectbObjectphasePhase): Boolean
if a  GeneralNumber and b  GeneralNumber then
return generalNumberCompare(ab) = equal
else return a = b
end if
end proc;

Binary Bitwise Operators

Syntax

BitwiseAndExpression 
   EqualityExpression
|  BitwiseAndExpression & EqualityExpression
BitwiseXorExpression 
   BitwiseAndExpression
|  BitwiseXorExpression ^ BitwiseAndExpression
BitwiseOrExpression 
   BitwiseXorExpression
|  BitwiseOrExpression | BitwiseXorExpression

Validation

Validate[BitwiseAndExpression] (cxtContextenvEnvironment) propagates the call to Validate to nonterminals in the expansion of BitwiseAndExpression.
Validate[BitwiseXorExpression] (cxtContextenvEnvironment) propagates the call to Validate to nonterminals in the expansion of BitwiseXorExpression.
Validate[BitwiseOrExpression] (cxtContextenvEnvironment) propagates the call to Validate to nonterminals in the expansion of BitwiseOrExpression.

Setup

Setup[BitwiseAndExpression] () propagates the call to Setup to nonterminals in the expansion of BitwiseAndExpression.
Setup[BitwiseXorExpression] () propagates the call to Setup to nonterminals in the expansion of BitwiseXorExpression.
Setup[BitwiseOrExpression] () propagates the call to Setup to nonterminals in the expansion of BitwiseOrExpression.

Evaluation

proc Eval[BitwiseAndExpression] (envEnvironmentphasePhase): ObjOrRef
[BitwiseAndExpression  EqualityExpressiondo
return Eval[EqualityExpression](envphase);
[BitwiseAndExpression0  BitwiseAndExpression1 & EqualityExpressiondo
aObject  readReference(Eval[BitwiseAndExpression1](envphase), phase);
bObject  readReference(Eval[EqualityExpression](envphase), phase);
return bitAnd(abphase)
end proc;
proc Eval[BitwiseXorExpression] (envEnvironmentphasePhase): ObjOrRef
[BitwiseXorExpression  BitwiseAndExpressiondo
return Eval[BitwiseAndExpression](envphase);
[BitwiseXorExpression0  BitwiseXorExpression1 ^ BitwiseAndExpressiondo
aObject  readReference(Eval[BitwiseXorExpression1](envphase), phase);
bObject  readReference(Eval[BitwiseAndExpression](envphase), phase);
return bitXor(abphase)
end proc;
proc Eval[BitwiseOrExpression] (envEnvironmentphasePhase): ObjOrRef
[BitwiseOrExpression  BitwiseXorExpressiondo
return Eval[BitwiseXorExpression](envphase);
[BitwiseOrExpression0  BitwiseOrExpression1 | BitwiseXorExpressiondo
aObject  readReference(Eval[BitwiseOrExpression1](envphase), phase);
bObject  readReference(Eval[BitwiseXorExpression](envphase), phase);
return bitOr(abphase)
end proc;
proc bitAnd(aObjectbObjectphasePhase): GeneralNumber
xGeneralNumber  objectToGeneralNumber(aphase);
yGeneralNumber  objectToGeneralNumber(bphase);
if x  Long  ULong or y  Long  ULong then
i: {–263 ... 263 – 1}  signedWrap64(truncateToInteger(x));
j: {–263 ... 263 – 1}  signedWrap64(truncateToInteger(y));
k: {–263 ... 263 – 1}  bitwiseAnd(ij);
if x  ULong or y  ULong then return (unsignedWrap64(k))ulong
else return klong
end if
else
i: {–231 ... 231 – 1}  signedWrap32(truncateToInteger(x));
j: {–231 ... 231 – 1}  signedWrap32(truncateToInteger(y));
return (bitwiseAnd(ij))f64
end if
end proc;
proc bitXor(aObjectbObjectphasePhase): GeneralNumber
xGeneralNumber  objectToGeneralNumber(aphase);
yGeneralNumber  objectToGeneralNumber(bphase);
if x  Long  ULong or y  Long  ULong then
i: {–263 ... 263 – 1}  signedWrap64(truncateToInteger(x));
j: {–263 ... 263 – 1}  signedWrap64(truncateToInteger(y));
k: {–263 ... 263 – 1}  bitwiseXor(ij);
if x  ULong or y  ULong then return (unsignedWrap64(k))ulong
else return klong
end if
else
i: {–231 ... 231 – 1}  signedWrap32(truncateToInteger(x));
j: {–231 ... 231 – 1}  signedWrap32(truncateToInteger(y));
return (bitwiseXor(ij))f64
end if
end proc;
proc bitOr(aObjectbObjectphasePhase): GeneralNumber
xGeneralNumber  objectToGeneralNumber(aphase);
yGeneralNumber  objectToGeneralNumber(bphase);
if x  Long  ULong or y  Long  ULong then
i: {–263 ... 263 – 1}  signedWrap64(truncateToInteger(x));
j: {–263 ... 263 – 1}  signedWrap64(truncateToInteger(y));
k: {–263 ... 263 – 1}  bitwiseOr(ij);
if x  ULong or y  ULong then return (unsignedWrap64(k))ulong
else return klong
end if
else
i: {–231 ... 231 – 1}  signedWrap32(truncateToInteger(x));
j: {–231 ... 231 – 1}  signedWrap32(truncateToInteger(y));
return (bitwiseOr(ij))f64
end if
end proc;

Binary Logical Operators

Syntax

LogicalAndExpression 
   BitwiseOrExpression
|  LogicalAndExpression && BitwiseOrExpression
LogicalXorExpression 
   LogicalAndExpression
|  LogicalXorExpression ^^ LogicalAndExpression
LogicalOrExpression 
   LogicalXorExpression
|  LogicalOrExpression || LogicalXorExpression

Validation

Validate[LogicalAndExpression] (cxtContextenvEnvironment) propagates the call to Validate to nonterminals in the expansion of LogicalAndExpression.
Validate[LogicalXorExpression] (cxtContextenvEnvironment) propagates the call to Validate to nonterminals in the expansion of LogicalXorExpression.
Validate[LogicalOrExpression] (cxtContextenvEnvironment) propagates the call to Validate to nonterminals in the expansion of LogicalOrExpression.

Setup

Setup[LogicalAndExpression] () propagates the call to Setup to nonterminals in the expansion of LogicalAndExpression.
Setup[LogicalXorExpression] () propagates the call to Setup to nonterminals in the expansion of LogicalXorExpression.
Setup[LogicalOrExpression] () propagates the call to Setup to nonterminals in the expansion of LogicalOrExpression.

Evaluation

proc Eval[LogicalAndExpression] (envEnvironmentphasePhase): ObjOrRef
[LogicalAndExpression  BitwiseOrExpressiondo
return Eval[BitwiseOrExpression](envphase);
[LogicalAndExpression0  LogicalAndExpression1 && BitwiseOrExpressiondo
aObject  readReference(Eval[LogicalAndExpression1](envphase), phase);
if objectToBoolean(athen
return readReference(Eval[BitwiseOrExpression](envphase), phase)
else return a
end if
end proc;
proc Eval[LogicalXorExpression] (envEnvironmentphasePhase): ObjOrRef
[LogicalXorExpression  LogicalAndExpressiondo
return Eval[LogicalAndExpression](envphase);
[LogicalXorExpression0  LogicalXorExpression1 ^^ LogicalAndExpressiondo
aObject  readReference(Eval[LogicalXorExpression1](envphase), phase);
bObject  readReference(Eval[LogicalAndExpression](envphase), phase);
baBoolean  objectToBoolean(a);
bbBoolean  objectToBoolean(b);
return ba xor bb
end proc;
proc Eval[LogicalOrExpression] (envEnvironmentphasePhase): ObjOrRef
[LogicalOrExpression  LogicalXorExpressiondo
return Eval[LogicalXorExpression](envphase);
[LogicalOrExpression0  LogicalOrExpression1 || LogicalXorExpressiondo
aObject  readReference(Eval[LogicalOrExpression1](envphase), phase);
if objectToBoolean(athen return a
else return readReference(Eval[LogicalXorExpression](envphase), phase)
end if
end proc;

Conditional Operator

Syntax

ConditionalExpression 
   LogicalOrExpression
|  LogicalOrExpression ? AssignmentExpression : AssignmentExpression
NonAssignmentExpression 
   LogicalOrExpression
|  LogicalOrExpression ? NonAssignmentExpression : NonAssignmentExpression

Validation

Validate[ConditionalExpression] (cxtContextenvEnvironment) propagates the call to Validate to nonterminals in the expansion of ConditionalExpression.
Validate[NonAssignmentExpression] (cxtContextenvEnvironment) propagates the call to Validate to nonterminals in the expansion of NonAssignmentExpression.

Setup

Setup[ConditionalExpression] () propagates the call to Setup to nonterminals in the expansion of ConditionalExpression.
Setup[NonAssignmentExpression] () propagates the call to Setup to nonterminals in the expansion of NonAssignmentExpression.

Evaluation

proc Eval[ConditionalExpression] (envEnvironmentphasePhase): ObjOrRef
[ConditionalExpression  LogicalOrExpressiondo
return Eval[LogicalOrExpression](envphase);
[ConditionalExpression  LogicalOrExpression ? AssignmentExpression1 : AssignmentExpression2do
aObject  readReference(Eval[LogicalOrExpression](envphase), phase);
if objectToBoolean(athen
return readReference(Eval[AssignmentExpression1](envphase), phase)
else return readReference(Eval[AssignmentExpression2](envphase), phase)
end if
end proc;
proc Eval[NonAssignmentExpression] (envEnvironmentphasePhase): ObjOrRef
[NonAssignmentExpression  LogicalOrExpressiondo
return Eval[LogicalOrExpression](envphase);
[NonAssignmentExpression0  LogicalOrExpression ? NonAssignmentExpression1 : NonAssignmentExpression2do
aObject  readReference(Eval[LogicalOrExpression](envphase), phase);
if objectToBoolean(athen
return readReference(Eval[NonAssignmentExpression1](envphase), phase)
else return readReference(Eval[NonAssignmentExpression2](envphase), phase)
end if
end proc;

Assignment Operators

Syntax

AssignmentExpression 
   ConditionalExpression
|  PostfixExpression = AssignmentExpression
CompoundAssignment 
   *=
|  /=
|  %=
|  +=
|  -=
|  <<=
|  >>=
|  >>>=
|  &=
|  ^=
|  |=
LogicalAssignment 
   &&=
|  ^^=
|  ||=

Semantics

tag andEq;
tag xorEq;
tag orEq;

Validation

proc Validate[AssignmentExpression] (cxtContextenvEnvironment)
[AssignmentExpression  ConditionalExpressiondo
Evaluate Validate[ConditionalExpression](cxtenv) and ignore its result;
[AssignmentExpression0  PostfixExpression = AssignmentExpression1do
Evaluate Validate[PostfixExpression](cxtenv) and ignore its result;
Evaluate Validate[AssignmentExpression1](cxtenv) and ignore its result;
[AssignmentExpression0  PostfixExpression CompoundAssignment AssignmentExpression1do
Evaluate Validate[PostfixExpression](cxtenv) and ignore its result;
Evaluate Validate[AssignmentExpression1](cxtenv) and ignore its result;
[AssignmentExpression0  PostfixExpression LogicalAssignment AssignmentExpression1do
Evaluate Validate[PostfixExpression](cxtenv) and ignore its result;
Evaluate Validate[AssignmentExpression1](cxtenv) and ignore its result
end proc;

Setup

proc Setup[AssignmentExpression] ()
[AssignmentExpression  ConditionalExpressiondo
Evaluate Setup[ConditionalExpression]() and ignore its result;
[AssignmentExpression0  PostfixExpression = AssignmentExpression1do
Evaluate Setup[PostfixExpression]() and ignore its result;
Evaluate Setup[AssignmentExpression1]() and ignore its result;
[AssignmentExpression0  PostfixExpression CompoundAssignment AssignmentExpression1do
Evaluate Setup[PostfixExpression]() and ignore its result;
Evaluate Setup[AssignmentExpression1]() and ignore its result;
[AssignmentExpression0  PostfixExpression LogicalAssignment AssignmentExpression1do
Evaluate Setup[PostfixExpression]() and ignore its result;
Evaluate Setup[AssignmentExpression1]() and ignore its result
end proc;

Evaluation

proc Eval[AssignmentExpression] (envEnvironmentphasePhase): ObjOrRef
[AssignmentExpression  ConditionalExpressiondo
return Eval[ConditionalExpression](envphase);
[AssignmentExpression0  PostfixExpression = AssignmentExpression1do
if phase = compile then
throw a ConstantError exception — assignment cannot be used in a constant expression
end if;
raObjOrRef  Eval[PostfixExpression](envphase);
bObject  readReference(Eval[AssignmentExpression1](envphase), phase);
Evaluate writeReference(rabphase) and ignore its result;
return b;
[AssignmentExpression0  PostfixExpression CompoundAssignment AssignmentExpression1do
if phase = compile then
throw a ConstantError exception — assignment cannot be used in a constant expression
end if;
rLeftObjOrRef  Eval[PostfixExpression](envphase);
oLeftObject  readReference(rLeftphase);
oRightObject  readReference(Eval[AssignmentExpression1](envphase), phase);
resultObject  Op[CompoundAssignment](oLeftoRightphase);
Evaluate writeReference(rLeftresultphase) and ignore its result;
return result;
[AssignmentExpression0  PostfixExpression LogicalAssignment AssignmentExpression1do
if phase = compile then
throw a ConstantError exception — assignment cannot be used in a constant expression
end if;
rLeftObjOrRef  Eval[PostfixExpression](envphase);
oLeftObject  readReference(rLeftphase);
bLeftBoolean  objectToBoolean(oLeft);
resultObject  oLeft;
case Operator[LogicalAssignmentof
{andEqdo
if bLeft then
result  readReference(Eval[AssignmentExpression1](envphase), phase)
end if;
{xorEqdo
bRightBoolean  objectToBoolean(readReference(Eval[AssignmentExpression1](envphase), phase));
result  bLeft xor bRight;
{orEqdo
if not bLeft then
result  readReference(Eval[AssignmentExpression1](envphase), phase)
end if
end case;
Evaluate writeReference(rLeftresultphase) and ignore its result;
return result
end proc;
Op[CompoundAssignment]: Object  Object  Phase  Object;
Op[CompoundAssignment  *=] = multiply;
Op[CompoundAssignment  /=] = divide;
Op[CompoundAssignment  %=] = remainder;
Op[CompoundAssignment  +=] = add;
Op[CompoundAssignment  -=] = subtract;
Op[CompoundAssignment  <<=] = shiftLeft;
Op[CompoundAssignment  >>=] = shiftRight;
Op[CompoundAssignment  >>>=] = shiftRightUnsigned;
Op[CompoundAssignment  &=] = bitAnd;
Op[CompoundAssignment  ^=] = bitXor;
Op[CompoundAssignment  |=] = bitOr;
Operator[LogicalAssignment]: {andEqxorEqorEq};
Operator[LogicalAssignment  &&=] = andEq;
Operator[LogicalAssignment  ^^=] = xorEq;
Operator[LogicalAssignment  ||=] = orEq;

Comma Expressions

Syntax

ListExpression 
   AssignmentExpression
|  ListExpression , AssignmentExpression

Validation

Validate[ListExpression] (cxtContextenvEnvironment) propagates the call to Validate to nonterminals in the expansion of ListExpression.

Setup

Setup[ListExpression] () propagates the call to Setup to nonterminals in the expansion of ListExpression.

Evaluation

proc Eval[ListExpression] (envEnvironmentphasePhase): ObjOrRef
[ListExpression  AssignmentExpressiondo
return Eval[AssignmentExpression](envphase);
[ListExpression0  ListExpression1 , AssignmentExpressiondo
Evaluate readReference(Eval[ListExpression1](envphase), phase) and ignore its result;
return readReference(Eval[AssignmentExpression](envphase), phase)
end proc;
proc EvalAsList[ListExpression] (envEnvironmentphasePhase): Object[]
[ListExpression  AssignmentExpressiondo
eltObject  readReference(Eval[AssignmentExpression](envphase), phase);
return [elt];
[ListExpression0  ListExpression1 , AssignmentExpressiondo
eltsObject[]  EvalAsList[ListExpression1](envphase);
eltObject  readReference(Eval[AssignmentExpression](envphase), phase);
return elts  [elt]
end proc;

Type Expressions

Syntax

TypeExpression  NonAssignmentExpression

Validation

Validate[TypeExpression] (cxtContextenvEnvironment) propagates the call to Validate to nonterminals in the expansion of TypeExpression.

Setup and Evaluation

proc SetupAndEval[TypeExpression  NonAssignmentExpression] (envEnvironment): Class
Evaluate Setup[NonAssignmentExpression]() and ignore its result;
oObject  readReference(Eval[NonAssignmentExpression](envcompile), compile);
return objectToClass(o)
end proc;

Statements

Syntax

  {abbrevnoShortIffull}
Statement 
   ExpressionStatement Semicolon
|  SuperStatement Semicolon
|  Block
|  LabeledStatement
|  IfStatement
|  DoStatement Semicolon
|  WhileStatement
|  ForStatement
|  WithStatement
|  ContinueStatement Semicolon
|  BreakStatement Semicolon
|  ReturnStatement Semicolon
|  ThrowStatement Semicolon
Substatement 
|  Statement
|  SimpleVariableDefinition Semicolon
|  Attributes [no line break] { Substatements }
Substatements 
   «empty»
SubstatementsPrefix 
   «empty»
Semicolonabbrev 
   ;
|  VirtualSemicolon
|  «empty»
SemicolonnoShortIf 
   ;
|  VirtualSemicolon
|  «empty»
Semicolonfull 
   ;
|  VirtualSemicolon

Validation

proc Validate[Statement] (cxtContextenvEnvironmentslLabel{}, jtJumpTargetspreinstBoolean)
[Statement  ExpressionStatement Semicolondo
Evaluate Validate[ExpressionStatement](cxtenv) and ignore its result;
[Statement  SuperStatement Semicolondo
Evaluate Validate[SuperStatement](cxtenv) and ignore its result;
[Statement  Blockdo
Evaluate Validate[Block](cxtenvjtpreinst) and ignore its result;
[Statement  LabeledStatementdo
Evaluate Validate[LabeledStatement](cxtenvsljt) and ignore its result;
[Statement  IfStatementdo
Evaluate Validate[IfStatement](cxtenvjt) and ignore its result;
[Statement  SwitchStatementdo
Evaluate Validate[SwitchStatement](cxtenvjt) and ignore its result;
[Statement  DoStatement Semicolondo
Evaluate Validate[DoStatement](cxtenvsljt) and ignore its result;
[Statement  WhileStatementdo
Evaluate Validate[WhileStatement](cxtenvsljt) and ignore its result;
[Statement  ForStatementdo
Evaluate Validate[ForStatement](cxtenvsljt) and ignore its result;
[Statement  WithStatementdo
Evaluate Validate[WithStatement](cxtenvjt) and ignore its result;
[Statement  ContinueStatement Semicolondo
Evaluate Validate[ContinueStatement](jt) and ignore its result;
[Statement  BreakStatement Semicolondo
Evaluate Validate[BreakStatement](jt) and ignore its result;
[Statement  ReturnStatement Semicolondo
Evaluate Validate[ReturnStatement](cxtenv) and ignore its result;
[Statement  ThrowStatement Semicolondo
Evaluate Validate[ThrowStatement](cxtenv) and ignore its result;
[Statement  TryStatementdo
Evaluate Validate[TryStatement](cxtenvjt) and ignore its result
end proc;
Enabled[Substatement]: Boolean;
proc Validate[Substatement] (cxtContextenvEnvironmentslLabel{}, jtJumpTargets)
[Substatement  EmptyStatementdo nothing;
[Substatement  Statementdo
Evaluate Validate[Statement](cxtenvsljtfalse) and ignore its result;
[Substatement  SimpleVariableDefinition Semicolondo
Evaluate Validate[SimpleVariableDefinition](cxtenv) and ignore its result;
[Substatement  Attributes [no line break] { Substatements }do
Evaluate Validate[Attributes](cxtenv) and ignore its result;
Evaluate Setup[Attributes]() and ignore its result;
attrAttribute  Eval[Attributes](envcompile);
if attr  Boolean then
throw a TypeError exception — attributes other than true and false may be used in a statement but not a substatement
end if;
Enabled[Substatement attr;
if attr then Evaluate Validate[Substatements](cxtenvjt) and ignore its result
end if
end proc;
proc Validate[Substatements] (cxtContextenvEnvironmentjtJumpTargets)
[Substatements  «empty»] do nothing;
[Substatements  SubstatementsPrefix Substatementabbrevdo
Evaluate Validate[SubstatementsPrefix](cxtenvjt) and ignore its result;
Evaluate Validate[Substatementabbrev](cxtenv, {}, jt) and ignore its result
end proc;
proc Validate[SubstatementsPrefix] (cxtContextenvEnvironmentjtJumpTargets)
[SubstatementsPrefix  «empty»] do nothing;
[SubstatementsPrefix0  SubstatementsPrefix1 Substatementfulldo
Evaluate Validate[SubstatementsPrefix1](cxtenvjt) and ignore its result;
Evaluate Validate[Substatementfull](cxtenv, {}, jt) and ignore its result
end proc;

Setup

Setup[Statement] () propagates the call to Setup to nonterminals in the expansion of Statement.
proc Setup[Substatement] ()
[Substatement  EmptyStatementdo nothing;
[Substatement  Statementdo Evaluate Setup[Statement]() and ignore its result;
[Substatement  SimpleVariableDefinition Semicolondo
Evaluate Setup[SimpleVariableDefinition]() and ignore its result;
[Substatement  Attributes [no line break] { Substatements }do
if Enabled[Substatementthen
Evaluate Setup[Substatements]() and ignore its result
end if
end proc;
Setup[Substatements] () propagates the call to Setup to nonterminals in the expansion of Substatements.
Setup[SubstatementsPrefix] () propagates the call to Setup to nonterminals in the expansion of SubstatementsPrefix.
proc Setup[Semicolon] ()
[Semicolon  ;do nothing;
[Semicolon  VirtualSemicolondo nothing;
[Semicolonabbrev  «empty»] do nothing;
[SemicolonnoShortIf  «empty»] do nothing
end proc;

Evaluation

proc Eval[Statement] (envEnvironmentdObject): Object
[Statement  ExpressionStatement Semicolondo
return Eval[ExpressionStatement](env);
[Statement  SuperStatement Semicolondo return Eval[SuperStatement](env);
[Statement  Blockdo return Eval[Block](envd);
[Statement  LabeledStatementdo return Eval[LabeledStatement](envd);
[Statement  IfStatementdo return Eval[IfStatement](envd);
[Statement  SwitchStatementdo return Eval[SwitchStatement](envd);
[Statement  DoStatement Semicolondo return Eval[DoStatement](envd);
[Statement  WhileStatementdo return Eval[WhileStatement](envd);
[Statement  ForStatementdo return Eval[ForStatement](envd);
[Statement  WithStatementdo return Eval[WithStatement](envd);
[Statement  ContinueStatement Semicolondo
return Eval[ContinueStatement](envd);
[Statement  BreakStatement Semicolondo return Eval[BreakStatement](envd);
[Statement  ReturnStatement Semicolondo return Eval[ReturnStatement](env);
[Statement  ThrowStatement Semicolondo return Eval[ThrowStatement](env);
[Statement  TryStatementdo return Eval[TryStatement](envd)
end proc;
proc Eval[Substatement] (envEnvironmentdObject): Object
[Substatement  EmptyStatementdo return d;
[Substatement  Statementdo return Eval[Statement](envd);
[Substatement  SimpleVariableDefinition Semicolondo
return Eval[SimpleVariableDefinition](envd);
[Substatement  Attributes [no line break] { Substatements }do
if Enabled[Substatementthen return Eval[Substatements](envd)
else return d
end if
end proc;
proc Eval[Substatements] (envEnvironmentdObject): Object
[Substatements  «empty»] do return d;
[Substatements  SubstatementsPrefix Substatementabbrevdo
oObject  Eval[SubstatementsPrefix](envd);
return Eval[Substatementabbrev](envo)
end proc;
proc Eval[SubstatementsPrefix] (envEnvironmentdObject): Object
[SubstatementsPrefix  «empty»] do return d;
[SubstatementsPrefix0  SubstatementsPrefix1 Substatementfulldo
oObject  Eval[SubstatementsPrefix1](envd);
return Eval[Substatementfull](envo)
end proc;

Empty Statement

Syntax

EmptyStatement  ;

Expression Statement

Syntax

ExpressionStatement  [lookahead{function{}] ListExpressionallowIn

Validation

Validate[ExpressionStatement] (cxtContextenvEnvironment) propagates the call to Validate to nonterminals in the expansion of ExpressionStatement.

Setup

Setup[ExpressionStatement] () propagates the call to Setup to nonterminals in the expansion of ExpressionStatement.

Evaluation

proc Eval[ExpressionStatement  [lookahead{function{}] ListExpressionallowIn] (envEnvironment): Object
return readReference(Eval[ListExpressionallowIn](envrun), run)
end proc;

Super Statement

Syntax

SuperStatement  super Arguments

Validation

proc Validate[SuperStatement  super Arguments] (cxtContextenvEnvironment)
frameParameterFrameOpt  getEnclosingParameterFrame(env);
if frame = none or frame.kind  constructorFunction then
throw a SyntaxError exception — a super statement is meaningful only inside a constructor
end if;
Evaluate Validate[Arguments](cxtenv) and ignore its result;
frame.callsSuperconstructor  true
end proc;

Setup

Setup[SuperStatement] () propagates the call to Setup to nonterminals in the expansion of SuperStatement.

Evaluation

proc Eval[SuperStatement  super Arguments] (envEnvironment): Object
frameParameterFrameOpt  getEnclosingParameterFrame(env);
note  Validate already ensured that frame  none and frame.kind = constructorFunction.
argsObject[]  Eval[Arguments](envrun);
if frame.superconstructorCalled = true then
throw a ReferenceError exception — the superconstructor cannot be called twice
end if;
cClass  getEnclosingClass(env);
thisObjectOpt  frame.this;
note  this  SimpleInstance;
Evaluate callInit(thisc.superargsrun) and ignore its result;
frame.superconstructorCalled  true;
return this
end proc;

Block Statement

Syntax

Block  { Directives }

Validation

CompileFrame[Block]: LocalFrame;
Preinstantiate[Block]: Boolean;
proc ValidateUsingFrame[Block  { Directives }] (cxtContextenvEnvironmentjtJumpTargetspreinstBooleanframeFrame)
localCxtContext  new Contextstrictcxt.strictopenNamespacescxt.openNamespaces;
Evaluate Validate[Directives](localCxt[frame]  envjtpreinstnone) and ignore its result
end proc;
proc Validate[Block  { Directives }] (cxtContextenvEnvironmentjtJumpTargetspreinstBoolean)
compileFrameLocalFrame  new LocalFramelocalBindings: {};
CompileFrame[Block compileFrame;
Preinstantiate[Block preinst;
Evaluate ValidateUsingFrame[Block](cxtenvjtpreinstcompileFrame) and ignore its result
end proc;

Setup

Setup[Block] () propagates the call to Setup to nonterminals in the expansion of Block.

Evaluation

proc Eval[Block  { Directives }] (envEnvironmentdObject): Object
compileFrameLocalFrame  CompileFrame[Block];
runtimeFrameLocalFrame;
if Preinstantiate[Blockthen runtimeFrame  compileFrame
else runtimeFrame  instantiateLocalFrame(compileFrameenv)
end if;
return Eval[Directives]([runtimeFrame]  envd)
end proc;
proc EvalUsingFrame[Block  { Directives }] (envEnvironmentframeFramedObject): Object
return Eval[Directives]([frame]  envd)
end proc;

Labeled Statements

Syntax

LabeledStatement  Identifier : Substatement

Validation

proc Validate[LabeledStatement  Identifier : Substatement] (cxtContextenvEnvironmentslLabel{}, jtJumpTargets)
nameString  Name[Identifier];
if name  jt.breakTargets then
throw a SyntaxError exception — nesting labeled statements with the same label is not permitted
end if;
jt2JumpTargets  JumpTargetsbreakTargetsjt.breakTargets  {name}, continueTargetsjt.continueTargets;
Evaluate Validate[Substatement](cxtenvsl  {name}, jt2) and ignore its result
end proc;

Setup

proc Setup[LabeledStatement  Identifier : Substatement] ()
Evaluate Setup[Substatement]() and ignore its result
end proc;

Evaluation

proc Eval[LabeledStatement  Identifier : Substatement] (envEnvironmentdObject): Object
try return Eval[Substatement](envd)
catch xSemanticException do
if x  Break and x.label = Name[Identifierthen return x.value
else throw x
end if
end try
end proc;

If Statement

Syntax

IfStatementabbrev 
|  if ParenListExpression SubstatementnoShortIf else Substatementabbrev
IfStatementfull 
|  if ParenListExpression SubstatementnoShortIf else Substatementfull
IfStatementnoShortIf  if ParenListExpression SubstatementnoShortIf else SubstatementnoShortIf

Validation

proc Validate[IfStatement] (cxtContextenvEnvironmentjtJumpTargets)
[IfStatementabbrev  if ParenListExpression Substatementabbrevdo
Evaluate Validate[ParenListExpression](cxtenv) and ignore its result;
Evaluate Validate[Substatementabbrev](cxtenv, {}, jt) and ignore its result;
[IfStatementfull  if ParenListExpression Substatementfulldo
Evaluate Validate[ParenListExpression](cxtenv) and ignore its result;
Evaluate Validate[Substatementfull](cxtenv, {}, jt) and ignore its result;
[IfStatement  if ParenListExpression SubstatementnoShortIf1 else Substatement2do
Evaluate Validate[ParenListExpression](cxtenv) and ignore its result;
Evaluate Validate[SubstatementnoShortIf1](cxtenv, {}, jt) and ignore its result;
Evaluate Validate[Substatement2](cxtenv, {}, jt) and ignore its result
end proc;

Setup

Setup[IfStatement] () propagates the call to Setup to nonterminals in the expansion of IfStatement.

Evaluation

proc Eval[IfStatement] (envEnvironmentdObject): Object
[IfStatementabbrev  if ParenListExpression Substatementabbrevdo
oObject  readReference(Eval[ParenListExpression](envrun), run);
if objectToBoolean(othen return Eval[Substatementabbrev](envd)
else return d
end if;
[IfStatementfull  if ParenListExpression Substatementfulldo
oObject  readReference(Eval[ParenListExpression](envrun), run);
if objectToBoolean(othen return Eval[Substatementfull](envd)
else return d
end if;
[IfStatement  if ParenListExpression SubstatementnoShortIf1 else Substatement2do
oObject  readReference(Eval[ParenListExpression](envrun), run);
if objectToBoolean(othen return Eval[SubstatementnoShortIf1](envd)
else return Eval[Substatement2](envd)
end if
end proc;

Switch Statement

Semantics

tuple SwitchKey
keyObject
end tuple;
SwitchGuard = SwitchKey  {default Object;

Syntax

SwitchStatement  switch ParenListExpression { CaseElements }
CaseElements 
   «empty»
CaseElementsPrefix 
   «empty»
CaseElement 
   Directive
CaseLabel 
   case ListExpressionallowIn :
|  default :

Validation

CompileFrame[SwitchStatement]: LocalFrame;
proc Validate[SwitchStatement  switch ParenListExpression { CaseElements }] (cxtContextenvEnvironmentjtJumpTargets)
if NDefaults[CaseElements] > 1 then
throw a SyntaxError exception — a case statement may have at most one default clause
end if;
Evaluate Validate[ParenListExpression](cxtenv) and ignore its result;
jt2JumpTargets  JumpTargetsbreakTargetsjt.breakTargets  {default}, continueTargetsjt.continueTargets;
compileFrameLocalFrame  new LocalFramelocalBindings: {};
CompileFrame[SwitchStatement compileFrame;
localCxtContext  new Contextstrictcxt.strictopenNamespacescxt.openNamespaces;
Evaluate Validate[CaseElements](localCxt[compileFrame]  envjt2) and ignore its result
end proc;
NDefaults[CaseElements]: Integer;
NDefaults[CaseElements  «empty»] = 0;
NDefaults[CaseElements  CaseLabel] = NDefaults[CaseLabel];
NDefaults[CaseElements  CaseLabel CaseElementsPrefix CaseElementabbrev] = NDefaults[CaseLabel] + NDefaults[CaseElementsPrefix] + NDefaults[CaseElementabbrev];
Validate[CaseElements] (cxtContextenvEnvironmentjtJumpTargets) propagates the call to Validate to nonterminals in the expansion of CaseElements.
NDefaults[CaseElementsPrefix  «empty»] = 0;
NDefaults[CaseElementsPrefix0  CaseElementsPrefix1 CaseElementfull] = NDefaults[CaseElementsPrefix1] + NDefaults[CaseElementfull];
Validate[CaseElementsPrefix] (cxtContextenvEnvironmentjtJumpTargets) propagates the call to Validate to nonterminals in the expansion of CaseElementsPrefix.
NDefaults[CaseElement]: Integer;
NDefaults[CaseElement  Directive] = 0;
NDefaults[CaseElement  CaseLabel] = NDefaults[CaseLabel];
proc Validate[CaseElement] (cxtContextenvEnvironmentjtJumpTargets)
[CaseElement  Directivedo
Evaluate Validate[Directive](cxtenvjtfalsenone) and ignore its result;
[CaseElement  CaseLabeldo
Evaluate Validate[CaseLabel](cxtenvjt) and ignore its result
end proc;
NDefaults[CaseLabel]: Integer;
NDefaults[CaseLabel  case ListExpressionallowIn :] = 0;
NDefaults[CaseLabel  default :] = 1;
proc Validate[CaseLabel] (cxtContextenvEnvironmentjtJumpTargets)
[CaseLabel  case ListExpressionallowIn :do
Evaluate Validate[ListExpressionallowIn](cxtenv) and ignore its result;
[CaseLabel  default :do nothing
end proc;

Setup

Setup[SwitchStatement] () propagates the call to Setup to nonterminals in the expansion of SwitchStatement.
Setup[CaseElements] () propagates the call to Setup to nonterminals in the expansion of CaseElements.
Setup[CaseElementsPrefix] () propagates the call to Setup to nonterminals in the expansion of CaseElementsPrefix.
Setup[CaseElement] () propagates the call to Setup to nonterminals in the expansion of CaseElement.
Setup[CaseLabel] () propagates the call to Setup to nonterminals in the expansion of CaseLabel.

Evaluation

proc Eval[SwitchStatement  switch ParenListExpression { CaseElements }] (envEnvironmentdObject): Object
keyObject  readReference(Eval[ParenListExpression](envrun), run);
compileFrameLocalFrame  CompileFrame[SwitchStatement];
runtimeFrameLocalFrame  instantiateLocalFrame(compileFrameenv);
runtimeEnvEnvironment  [runtimeFrame]  env;
resultSwitchGuard  Eval[CaseElements](runtimeEnvSwitchKeykeykeyd);
if result  Object then return result end if;
note  result = SwitchKeykeykey;
result  Eval[CaseElements](runtimeEnvdefaultd);
if result  Object then return result end if;
note  result = default;
return d
end proc;
proc Eval[CaseElements] (envEnvironmentguardSwitchGuarddObject): SwitchGuard
[CaseElements  «empty»] do return guard;
[CaseElements  CaseLabeldo return Eval[CaseLabel](envguardd);
[CaseElements  CaseLabel CaseElementsPrefix CaseElementabbrevdo
guard2SwitchGuard  Eval[CaseLabel](envguardd);
guard3SwitchGuard  Eval[CaseElementsPrefix](envguard2d);
return Eval[CaseElementabbrev](envguard3d)
end proc;
proc Eval[CaseElementsPrefix] (envEnvironmentguardSwitchGuarddObject): SwitchGuard
[CaseElementsPrefix  «empty»] do return guard;
[CaseElementsPrefix0  CaseElementsPrefix1 CaseElementfulldo
guard2SwitchGuard  Eval[CaseElementsPrefix1](envguardd);
return Eval[CaseElementfull](envguard2d)
end proc;
proc Eval[CaseElement] (envEnvironmentguardSwitchGuarddObject): SwitchGuard
[CaseElement  Directivedo
case guard of
SwitchKey  {defaultdo return guard;
Object do return Eval[Directive](envguard)
end case;
[CaseElement  CaseLabeldo return Eval[CaseLabel](envguardd)
end proc;
proc Eval[CaseLabel] (envEnvironmentguardSwitchGuarddObject): SwitchGuard
[CaseLabel  case ListExpressionallowIn :do
case guard of
{default Object do return guard;
labelObject  readReference(Eval[ListExpressionallowIn](envrun), run);
if isStrictlyEqual(guard.keylabelrunthen return d
else return guard
end if
end case;
[CaseLabel  default :do
case guard of
SwitchKey  Object do return guard;
{defaultdo return d
end case
end proc;

Do-While Statement

Syntax

DoStatement  do Substatementabbrev while ParenListExpression

Validation

Labels[DoStatement]: Label{};
proc Validate[DoStatement  do Substatementabbrev while ParenListExpression] (cxtContextenvEnvironmentslLabel{}, jtJumpTargets)
continueLabelsLabel{}  sl  {default};
Labels[DoStatement continueLabels;
jt2JumpTargets  JumpTargetsbreakTargetsjt.breakTargets  {default}, continueTargetsjt.continueTargets  continueLabels;
Evaluate Validate[Substatementabbrev](cxtenv, {}, jt2) and ignore its result;
Evaluate Validate[ParenListExpression](cxtenv) and ignore its result
end proc;

Setup

Setup[DoStatement] () propagates the call to Setup to nonterminals in the expansion of DoStatement.

Evaluation

proc Eval[DoStatement  do Substatementabbrev while ParenListExpression] (envEnvironmentdObject): Object
try
d1Object  d;
while true do
try d1  Eval[Substatementabbrev](envd1)
catch xSemanticException do
if x  Continue and x.label  Labels[DoStatementthen d1  x.value
else throw x
end if
end try;
oObject  readReference(Eval[ParenListExpression](envrun), run);
if not objectToBoolean(othen return d1 end if
end while
catch xSemanticException do
if x  Break and x.label = default then return x.value else throw x end if
end try
end proc;

While Statement

Syntax

WhileStatement  while ParenListExpression Substatement

Validation

Labels[WhileStatement]: Label{};
proc Validate[WhileStatement  while ParenListExpression Substatement] (cxtContextenvEnvironmentslLabel{}, jtJumpTargets)
continueLabelsLabel{}  sl  {default};
Labels[WhileStatement continueLabels;
jt2JumpTargets  JumpTargetsbreakTargetsjt.breakTargets  {default}, continueTargetsjt.continueTargets  continueLabels;
Evaluate Validate[ParenListExpression](cxtenv) and ignore its result;
Evaluate Validate[Substatement](cxtenv, {}, jt2) and ignore its result
end proc;

Setup

Setup[WhileStatement] () propagates the call to Setup to nonterminals in the expansion of WhileStatement.

Evaluation

proc Eval[WhileStatement  while ParenListExpression Substatement] (envEnvironmentdObject): Object
try
d1Object  d;
try d1  Eval[Substatement](envd1)
catch xSemanticException do
if x  Continue and x.label  Labels[WhileStatementthen d1  x.value
else throw x
end if
end try
end while;
return d1
catch xSemanticException do
if x  Break and x.label = default then return x.value else throw x end if
end try
end proc;

For Statements

Syntax

ForStatement 
   for ( ForInitializer ; OptionalExpression ; OptionalExpression ) Substatement
|  for ( ForInBinding in ListExpressionallowIn ) Substatement
ForInitializer 
   «empty»
|  Attributes [no line break] VariableDefinitionnoIn
ForInBinding 
|  Attributes [no line break] VariableDefinitionKind VariableBindingnoIn
OptionalExpression 
   ListExpressionallowIn
|  «empty»

Validation

Labels[ForStatement]: Label{};
CompileLocalFrame[ForStatement]: LocalFrame;
proc Validate[ForStatement] (cxtContextenvEnvironmentslLabel{}, jtJumpTargets)
[ForStatement  for ( ForInitializer ; OptionalExpression1 ; OptionalExpression2 ) Substatementdo
continueLabelsLabel{}  sl  {default};
Labels[ForStatement continueLabels;
jt2JumpTargets  JumpTargetsbreakTargetsjt.breakTargets  {default}, continueTargetsjt.continueTargets  continueLabels;
compileLocalFrameLocalFrame  new LocalFramelocalBindings: {};
CompileLocalFrame[ForStatement compileLocalFrame;
compileEnvEnvironment  [compileLocalFrame]  env;
Evaluate Validate[ForInitializer](cxtcompileEnv) and ignore its result;
Evaluate Validate[OptionalExpression1](cxtcompileEnv) and ignore its result;
Evaluate Validate[OptionalExpression2](cxtcompileEnv) and ignore its result;
Evaluate Validate[Substatement](cxtcompileEnv, {}, jt2) and ignore its result;
[ForStatement  for ( ForInBinding in ListExpressionallowIn ) Substatementdo
continueLabelsLabel{}  sl  {default};
Labels[ForStatement continueLabels;
jt2JumpTargets  JumpTargetsbreakTargetsjt.breakTargets  {default}, continueTargetsjt.continueTargets  continueLabels;
Evaluate Validate[ListExpressionallowIn](cxtenv) and ignore its result;
compileLocalFrameLocalFrame  new LocalFramelocalBindings: {};
CompileLocalFrame[ForStatement compileLocalFrame;
compileEnvEnvironment  [compileLocalFrame]  env;
Evaluate Validate[ForInBinding](cxtcompileEnv) and ignore its result;
Evaluate Validate[Substatement](cxtcompileEnv, {}, jt2) and ignore its result
end proc;
proc Validate[ForInitializer] (cxtContextenvEnvironment)
[ForInitializer  «empty»] do nothing;
[ForInitializer  ListExpressionnoIndo
Evaluate Validate[ListExpressionnoIn](cxtenv) and ignore its result;
[ForInitializer  VariableDefinitionnoIndo
Evaluate Validate[VariableDefinitionnoIn](cxtenvnone) and ignore its result;
[ForInitializer  Attributes [no line break] VariableDefinitionnoIndo
Evaluate Validate[Attributes](cxtenv) and ignore its result;
Evaluate Setup[Attributes]() and ignore its result;
attrAttribute  Eval[Attributes](envcompile);
Enabled[ForInitializer attr  false;
if attr  false then
Evaluate Validate[VariableDefinitionnoIn](cxtenvattr) and ignore its result
end if
end proc;
proc Validate[ForInBinding] (cxtContextenvEnvironment)
[ForInBinding  PostfixExpressiondo
Evaluate Validate[PostfixExpression](cxtenv) and ignore its result;
[ForInBinding  VariableDefinitionKind VariableBindingnoIndo
Evaluate Validate[VariableBindingnoIn](cxt, env, none, Immutable[VariableDefinitionKind], true) and ignore its result;
[ForInBinding  Attributes [no line break] VariableDefinitionKind VariableBindingnoIndo
Evaluate Validate[Attributes](cxtenv) and ignore its result;
Evaluate Setup[Attributes]() and ignore its result;
attrAttribute  Eval[Attributes](envcompile);
if attr = false then
throw an AttributeError exception — the false attribute canot be applied to a for-in variable definition
end if;
Evaluate Validate[VariableBindingnoIn](cxt, env, attr, Immutable[VariableDefinitionKind], true) and ignore its result
end proc;
Validate[OptionalExpression] (cxtContextenvEnvironment) propagates the call to Validate to nonterminals in the expansion of OptionalExpression.

Setup

Setup[ForStatement] () propagates the call to Setup to nonterminals in the expansion of ForStatement.
proc Setup[ForInitializer] ()
[ForInitializer  «empty»] do nothing;
[ForInitializer  ListExpressionnoIndo
Evaluate Setup[ListExpressionnoIn]() and ignore its result;
[ForInitializer  VariableDefinitionnoIndo
Evaluate Setup[VariableDefinitionnoIn]() and ignore its result;
[ForInitializer  Attributes [no line break] VariableDefinitionnoIndo
if Enabled[ForInitializerthen
Evaluate Setup[VariableDefinitionnoIn]() and ignore its result
end if
end proc;
proc Setup[ForInBinding] ()
[ForInBinding  PostfixExpressiondo
Evaluate Setup[PostfixExpression]() and ignore its result;
[ForInBinding  VariableDefinitionKind VariableBindingnoIndo
Evaluate Setup[VariableBindingnoIn]() and ignore its result;
[ForInBinding  Attributes [no line break] VariableDefinitionKind VariableBindingnoIndo
Evaluate Setup[VariableBindingnoIn]() and ignore its result
end proc;
Setup[OptionalExpression] () propagates the call to Setup to nonterminals in the expansion of OptionalExpression.

Evaluation

proc Eval[ForStatement] (envEnvironmentdObject): Object
[ForStatement  for ( ForInitializer ; OptionalExpression1 ; OptionalExpression2 ) Substatementdo
runtimeLocalFrameLocalFrame  instantiateLocalFrame(CompileLocalFrame[ForStatement], env);
runtimeEnvEnvironment  [runtimeLocalFrame]  env;
try
Evaluate Eval[ForInitializer](runtimeEnv) and ignore its result;
d1Object  d;
while objectToBoolean(readReference(Eval[OptionalExpression1](runtimeEnvrun), run)) do
try d1  Eval[Substatement](runtimeEnvd1)
catch xSemanticException do
if x  Continue and x.label  Labels[ForStatementthen
d1  x.value
else throw x
end if
end try;
Evaluate readReference(Eval[OptionalExpression2](runtimeEnvrun), run) and ignore its result
end while;
return d1
catch xSemanticException do
if x  Break and x.label = default then return x.value else throw x end if
end try;
[ForStatement  for ( ForInBinding in ListExpressionallowIn ) Substatementdo
try
oObject  readReference(Eval[ListExpressionallowIn](envrun), run);
cClass  objectType(o);
oldIndicesObject{}  c.enumerate(o);
remainingIndicesObject{}  oldIndices;
d1Object  d;
while remainingIndices  {} do
runtimeLocalFrameLocalFrame  instantiateLocalFrame(CompileLocalFrame[ForStatement], env);
runtimeEnvEnvironment  [runtimeLocalFrame]  env;
indexObject  any element of remainingIndices;
remainingIndices  remainingIndices – {index};
Evaluate WriteBinding[ForInBinding](runtimeEnvindex) and ignore its result;
try d1  Eval[Substatement](runtimeEnvd1)
catch xSemanticException do
if x  Continue and x.label  Labels[ForStatementthen
d1  x.value
else throw x
end if
end try;
newIndicesObject{}  c.enumerate(o);
if newIndices  oldIndices then
The implementation may, at its discretion, add none, some, or all of the objects in the set difference newIndicesoldIndices to remainingIndices;
The implementation may, at its discretion, remove none, some, or all of the objects in the set difference oldIndicesnewIndices from remainingIndices;
end if;
oldIndices  newIndices
end while;
return d1
catch xSemanticException do
if x  Break and x.label = default then return x.value else throw x end if
end try
end proc;
proc Eval[ForInitializer] (envEnvironment)
[ForInitializer  «empty»] do nothing;
[ForInitializer  ListExpressionnoIndo
Evaluate readReference(Eval[ListExpressionnoIn](envrun), run) and ignore its result;
[ForInitializer  VariableDefinitionnoIndo
Evaluate Eval[VariableDefinitionnoIn](envundefined) and ignore its result;
[ForInitializer  Attributes [no line break] VariableDefinitionnoIndo
if Enabled[ForInitializerthen
Evaluate Eval[VariableDefinitionnoIn](envundefined) and ignore its result
end if
end proc;
proc WriteBinding[ForInBinding] (envEnvironmentnewValueObject)
[ForInBinding  PostfixExpressiondo
rObjOrRef  Eval[PostfixExpression](envrun);
Evaluate writeReference(rnewValuerun) and ignore its result;
[ForInBinding  VariableDefinitionKind VariableBindingnoIndo
Evaluate WriteBinding[VariableBindingnoIn](envnewValue) and ignore its result;
[ForInBinding  Attributes [no line break] VariableDefinitionKind VariableBindingnoIndo
Evaluate WriteBinding[VariableBindingnoIn](envnewValue) and ignore its result
end proc;
proc Eval[OptionalExpression] (envEnvironmentphasePhase): ObjOrRef
[OptionalExpression  ListExpressionallowIndo
return Eval[ListExpressionallowIn](envphase);
[OptionalExpression  «empty»] do return true
end proc;

With Statement

Syntax

WithStatement  with ParenListExpression Substatement

Validation

CompileLocalFrame[WithStatement]: LocalFrame;
proc Validate[WithStatement  with ParenListExpression Substatement] (cxtContextenvEnvironmentjtJumpTargets)
Evaluate Validate[ParenListExpression](cxtenv) and ignore its result;
compileWithFrameWithFrame  new WithFramevaluenone;
compileLocalFrameLocalFrame  new LocalFramelocalBindings: {};
CompileLocalFrame[WithStatement compileLocalFrame;
compileEnvEnvironment  [compileLocalFrame]  [compileWithFrame]  env;
Evaluate Validate[Substatement](cxtcompileEnv, {}, jt) and ignore its result
end proc;

Setup

Setup[WithStatement] () propagates the call to Setup to nonterminals in the expansion of WithStatement.

Evaluation

proc Eval[WithStatement  with ParenListExpression Substatement] (envEnvironmentdObject): Object
valueObject  readReference(Eval[ParenListExpression](envrun), run);
runtimeWithFrameWithFrame  new WithFramevaluevalue;
runtimeLocalFrameLocalFrame  instantiateLocalFrame(CompileLocalFrame[WithStatement], [runtimeWithFrame]  env);
runtimeEnvEnvironment  [runtimeLocalFrame]  [runtimeWithFrame]  env;
return Eval[Substatement](runtimeEnvd)
end proc;

Continue and Break Statements

Syntax

ContinueStatement 
   continue
|  continue [no line break] Identifier
BreakStatement 
   break
|  break [no line break] Identifier

Validation

proc Validate[ContinueStatement] (jtJumpTargets)
[ContinueStatement  continuedo
if default  jt.continueTargets then
throw a SyntaxError exception — there is no enclosing statement to which to continue
end if;
[ContinueStatement  continue [no line break] Identifierdo
if Name[Identifier jt.continueTargets then
throw a SyntaxError exception — there is no enclosing labeled statement to which to continue
end if
end proc;
proc Validate[BreakStatement] (jtJumpTargets)
[BreakStatement  breakdo
if default  jt.breakTargets then
throw a SyntaxError exception — there is no enclosing statement to which to break
end if;
[BreakStatement  break [no line break] Identifierdo
if Name[Identifier jt.breakTargets then
throw a SyntaxError exception — there is no enclosing labeled statement to which to break
end if
end proc;

Setup

proc Setup[ContinueStatement] ()
[ContinueStatement  continuedo nothing;
[ContinueStatement  continue [no line break] Identifierdo nothing
end proc;
proc Setup[BreakStatement] ()
[BreakStatement  breakdo nothing;
[BreakStatement  break [no line break] Identifierdo nothing
end proc;

Evaluation

proc Eval[ContinueStatement] (envEnvironmentdObject): Object
[ContinueStatement  continuedo throw Continuevaluedlabeldefault;
[ContinueStatement  continue [no line break] Identifierdo
throw ContinuevaluedlabelName[Identifier]
end proc;
proc Eval[BreakStatement] (envEnvironmentdObject): Object
[BreakStatement  breakdo throw Breakvaluedlabeldefault;
[BreakStatement  break [no line break] Identifierdo
throw BreakvaluedlabelName[Identifier]
end proc;

Return Statement

Syntax

ReturnStatement 
   return
|  return [no line break] ListExpressionallowIn

Validation

proc Validate[ReturnStatement] (cxtContextenvEnvironment)
[ReturnStatement  returndo
if getEnclosingParameterFrame(env) = none then
throw a SyntaxError exception — a return statement must be located inside a function
end if;
[ReturnStatement  return [no line break] ListExpressionallowIndo
frameParameterFrameOpt  getEnclosingParameterFrame(env);
if frame = none then
throw a SyntaxError exception — a return statement must be located inside a function
end if;
if cannotReturnValue(framethen
throw a SyntaxError exception — a return statement inside a setter or constructor cannot return a value
end if;
Evaluate Validate[ListExpressionallowIn](cxtenv) and ignore its result
end proc;

Setup

Setup[ReturnStatement] () propagates the call to Setup to nonterminals in the expansion of ReturnStatement.

Evaluation

proc Eval[ReturnStatement] (envEnvironment): Object
[ReturnStatement  returndo throw Returnvalueundefined;
[ReturnStatement  return [no line break] ListExpressionallowIndo
aObject  readReference(Eval[ListExpressionallowIn](envrun), run);
throw Returnvaluea
end proc;
cannotReturnValue(frame) returns true if the function represented by frame cannot return a value because it is a setter or constructor.
proc cannotReturnValue(frameParameterFrame): Boolean
return frame.kind = constructorFunction or frame.handling = set
end proc;

Throw Statement

Syntax

ThrowStatement  throw [no line break] ListExpressionallowIn

Validation

Validate[ThrowStatement] (cxtContextenvEnvironment) propagates the call to Validate to nonterminals in the expansion of ThrowStatement.

Setup

Setup[ThrowStatement] () propagates the call to Setup to nonterminals in the expansion of ThrowStatement.

Evaluation

proc Eval[ThrowStatement  throw [no line break] ListExpressionallowIn] (envEnvironment): Object
aObject  readReference(Eval[ListExpressionallowIn](envrun), run);
throw a
end proc;

Try Statement

Syntax

TryStatement 
   try Block CatchClauses
|  try Block CatchClausesOpt finally Block
CatchClausesOpt 
   «empty»
CatchClauses 
CatchClause  catch ( Parameter ) Block

Validation

proc Validate[TryStatement] (cxtContextenvEnvironmentjtJumpTargets)
[TryStatement  try Block CatchClausesdo
Evaluate Validate[Block](cxtenvjtfalse) and ignore its result;
Evaluate Validate[CatchClauses](cxtenvjt) and ignore its result;
[TryStatement  try Block1 CatchClausesOpt finally Block2do
Evaluate Validate[Block1](cxtenvjtfalse) and ignore its result;
Evaluate Validate[CatchClausesOpt](cxtenvjt) and ignore its result;
Evaluate Validate[Block2](cxtenvjtfalse) and ignore its result
end proc;
Validate[CatchClausesOpt] (cxtContextenvEnvironmentjtJumpTargets) propagates the call to Validate to nonterminals in the expansion of CatchClausesOpt.
Validate[CatchClauses] (cxtContextenvEnvironmentjtJumpTargets) propagates the call to Validate to nonterminals in the expansion of CatchClauses.
CompileEnv[CatchClause]: Environment;
CompileFrame[CatchClause]: LocalFrame;
proc Validate[CatchClause  catch ( Parameter ) Block] (cxtContextenvEnvironmentjtJumpTargets)
compileFrameLocalFrame  new LocalFramelocalBindings: {};
compileEnvEnvironment  [compileFrame]  env;
CompileFrame[CatchClause compileFrame;
CompileEnv[CatchClause compileEnv;
Evaluate Validate[Parameter](cxtcompileEnvcompileFrame) and ignore its result;
Evaluate Validate[Block](cxtcompileEnvjtfalse) and ignore its result
end proc;

Setup

Setup[TryStatement] () propagates the call to Setup to nonterminals in the expansion of TryStatement.
Setup[CatchClausesOpt] () propagates the call to Setup to nonterminals in the expansion of CatchClausesOpt.
Setup[CatchClauses] () propagates the call to Setup to nonterminals in the expansion of CatchClauses.
proc Setup[CatchClause  catch ( Parameter ) Block] ()
Evaluate Setup[Parameter](CompileEnv[CatchClause], CompileFrame[CatchClause], none) and ignore its result;
Evaluate Setup[Block]() and ignore its result
end proc;

Evaluation

proc Eval[TryStatement] (envEnvironmentdObject): Object
[TryStatement  try Block CatchClausesdo
try return Eval[Block](envd)
catch xSemanticException do
if x  ControlTransfer then throw x
else
rObject  {reject Eval[CatchClauses](envx);
if r  reject then return r else throw x end if
end if
end try;
[TryStatement  try Block1 CatchClausesOpt finally Block2do
resultObjectOpt  none;
exceptionSemanticException  {none none;
try result  Eval[Block1](envd)
catch xSemanticException do exception  x
end try;
note  At this point exactly one of result and exception has a non-none value.
if exception  Object then
try
rObject  {reject Eval[CatchClausesOpt](envexception);
if r  reject then
note  The exception has been handled, so clear it.
result  r;
exception  none
end if
catch xSemanticException do
note  The catch clause threw another exception or ControlTransfer x, so replace the original exception with x.
exception  x
end try
end if;
note  The finally clause is executed even if the original block exited due to a ControlTransfer (break, continue, or return).
note  The finally clause is not inside a try-catch semantic statement, so if it throws another exception or ControlTransfer, then the original exception or ControlTransfer exception is dropped.
Evaluate Eval[Block2](envundefined) and ignore its result;
note  At this point exactly one of result and exception has a non-none value.
if exception  none then throw exception else return result end if
end proc;
proc Eval[CatchClausesOpt] (envEnvironmentexceptionObject): Object  {reject}
[CatchClausesOpt  «empty»] do return reject;
[CatchClausesOpt  CatchClausesdo return Eval[CatchClauses](envexception)
end proc;
proc Eval[CatchClauses] (envEnvironmentexceptionObject): Object  {reject}
[CatchClauses  CatchClausedo return Eval[CatchClause](envexception);
[CatchClauses0  CatchClauses1 CatchClausedo
rObject  {reject Eval[CatchClauses1](envexception);
if r  reject then return r else return Eval[CatchClause](envexceptionend if
end proc;
proc Eval[CatchClause  catch ( Parameter ) Block] (envEnvironmentexceptionObject): Object  {reject}
compileFrameLocalFrame  CompileFrame[CatchClause];
runtimeFrameLocalFrame  instantiateLocalFrame(compileFrameenv);
runtimeEnvEnvironment  [runtimeFrame]  env;
qnameQualifiedName  public::(Name[Parameter]);
vSingletonPropertyOpt  findLocalSingletonProperty(runtimeFrame, {qname}, write);
note  Validate created one local variable with the name in qname, so v  Variable.
if is(exceptionv.typethen
Evaluate writeSingletonProperty(vexceptionrun) and ignore its result;
return Eval[Block](runtimeEnvundefined)
else return reject
end if
end proc;

Directives

Syntax

Directive 
|  Statement
|  AnnotatableDirective
|  Attributes [no line break] AnnotatableDirective
|  Attributes [no line break] { Directives }
|  Pragma Semicolon
AnnotatableDirective 
   VariableDefinitionallowIn Semicolon
|  NamespaceDefinition Semicolon
|  ImportDirective Semicolon
|  UseDirective Semicolon
Directives 
   «empty»
DirectivesPrefix 
   «empty»

Validation

Enabled[Directive]: Boolean;
proc Validate[Directive] (cxtContext, envEnvironment, jtJumpTargets, preinstBoolean, attrAttributeOptNotFalse)
[Directive  EmptyStatementdo nothing;
[Directive  Statementdo
if attr  {nonetruethen
throw an AttributeError exception — an ordinary statement only permits the attributes true and false
end if;
Evaluate Validate[Statement](cxtenv, {}, jtpreinst) and ignore its result;
[Directive  AnnotatableDirectivedo
Evaluate Validate[AnnotatableDirective](cxtenvpreinstattr) and ignore its result;
[Directive  Attributes [no line break] AnnotatableDirectivedo
Evaluate Validate[Attributes](cxtenv) and ignore its result;
Evaluate Setup[Attributes]() and ignore its result;
attr2Attribute  Eval[Attributes](envcompile);
attr3Attribute  combineAttributes(attrattr2);
if attr3 = false then Enabled[Directive false
else
Enabled[Directive true;
Evaluate Validate[AnnotatableDirective](cxtenvpreinstattr3) and ignore its result
end if;
[Directive  Attributes [no line break] { Directives }do
Evaluate Validate[Attributes](cxtenv) and ignore its result;
Evaluate Setup[Attributes]() and ignore its result;
attr2Attribute  Eval[Attributes](envcompile);
attr3Attribute  combineAttributes(attrattr2);
if attr3 = false then Enabled[Directive false
else
Enabled[Directive true;
localCxtContext  new Contextstrictcxt.strictopenNamespacescxt.openNamespaces;
Evaluate Validate[Directives](localCxtenvjtpreinstattr3) and ignore its result
end if;
[Directive  Pragma Semicolondo
if attr  {nonetruethen Evaluate Validate[Pragma](cxt) and ignore its result
else
throw an AttributeError exception — a pragma directive only permits the attributes true and false
end if
end proc;
proc Validate[AnnotatableDirective] (cxtContextenvEnvironmentpreinstBooleanattrAttributeOptNotFalse)
[AnnotatableDirective  VariableDefinitionallowIn Semicolondo
Evaluate Validate[VariableDefinitionallowIn](cxtenvattr) and ignore its result;
[AnnotatableDirective  FunctionDefinitiondo
Evaluate Validate[FunctionDefinition](cxtenvpreinstattr) and ignore its result;
[AnnotatableDirective  ClassDefinitiondo
Evaluate Validate[ClassDefinition](cxtenvpreinstattr) and ignore its result;
[AnnotatableDirective  NamespaceDefinition Semicolondo
Evaluate Validate[NamespaceDefinition](cxtenvpreinstattr) and ignore its result;
[AnnotatableDirective  ImportDirective Semicolondo
Evaluate Validate[ImportDirective](cxtenvpreinstattr) and ignore its result;
[AnnotatableDirective  UseDirective Semicolondo
if attr  {nonetruethen
Evaluate Validate[UseDirective](cxtenv) and ignore its result
else
throw an AttributeError exception — a use directive only permits the attributes true and false
end if
end proc;
Validate[Directives] (cxtContext, envEnvironment, jtJumpTargets, preinstBoolean, attrAttributeOptNotFalse) propagates the call to Validate to nonterminals in the expansion of Directives.
Validate[DirectivesPrefix] (cxtContext, envEnvironment, jtJumpTargets, preinstBoolean, attrAttributeOptNotFalse) propagates the call to Validate to nonterminals in the expansion of DirectivesPrefix.

Setup

proc Setup[Directive] ()
[Directive  EmptyStatementdo nothing;
[Directive  Statementdo Evaluate Setup[Statement]() and ignore its result;
[Directive  AnnotatableDirectivedo
Evaluate Setup[AnnotatableDirective]() and ignore its result;
[Directive  Attributes [no line break] AnnotatableDirectivedo
if Enabled[Directivethen
Evaluate Setup[AnnotatableDirective]() and ignore its result
end if;
[Directive  Attributes [no line break] { Directives }do
if Enabled[Directivethen Evaluate Setup[Directives]() and ignore its result
end if;
[Directive  Pragma Semicolondo nothing
end proc;
proc Setup[AnnotatableDirective] ()
[AnnotatableDirective  VariableDefinitionallowIn Semicolondo
Evaluate Setup[VariableDefinitionallowIn]() and ignore its result;
[AnnotatableDirective  FunctionDefinitiondo
Evaluate Setup[FunctionDefinition]() and ignore its result;
[AnnotatableDirective  ClassDefinitiondo
Evaluate Setup[ClassDefinition]() and ignore its result;
[AnnotatableDirective  NamespaceDefinition Semicolondo nothing;
[AnnotatableDirective  ImportDirective Semicolondo nothing;
[AnnotatableDirective  UseDirective Semicolondo nothing
end proc;
Setup[Directives] () propagates the call to Setup to nonterminals in the expansion of Directives.
Setup[DirectivesPrefix] () propagates the call to Setup to nonterminals in the expansion of DirectivesPrefix.

Evaluation

proc Eval[Directive] (envEnvironmentdObject): Object
[Directive  EmptyStatementdo return d;
[Directive  Statementdo return Eval[Statement](envd);
[Directive  AnnotatableDirectivedo return Eval[AnnotatableDirective](envd);
[Directive  Attributes [no line break] AnnotatableDirectivedo
if Enabled[Directivethen return Eval[AnnotatableDirective](envd)
else return d
end if;
[Directive  Attributes [no line break] { Directives }do
if Enabled[Directivethen return Eval[Directives](envdelse return d end if;
[Directive  Pragma Semicolondo return d
end proc;
proc Eval[AnnotatableDirective] (envEnvironmentdObject): Object
[AnnotatableDirective  VariableDefinitionallowIn Semicolondo
return Eval[VariableDefinitionallowIn](envd);
[AnnotatableDirective  FunctionDefinitiondo return d;
[AnnotatableDirective  ClassDefinitiondo return Eval[ClassDefinition](envd);
[AnnotatableDirective  NamespaceDefinition Semicolondo return d;
[AnnotatableDirective  ImportDirective Semicolondo return d;
[AnnotatableDirective  UseDirective Semicolondo return d
end proc;
proc Eval[Directives] (envEnvironmentdObject): Object
[Directives  «empty»] do return d;
[Directives  DirectivesPrefix Directiveabbrevdo
oObject  Eval[DirectivesPrefix](envd);
return Eval[Directiveabbrev](envo)
end proc;
proc Eval[DirectivesPrefix] (envEnvironmentdObject): Object
[DirectivesPrefix  «empty»] do return d;
[DirectivesPrefix0  DirectivesPrefix1 Directivefulldo
oObject  Eval[DirectivesPrefix1](envd);
return Eval[Directivefull](envo)
end proc;

Attributes

Syntax

Attributes 
   Attribute
AttributeCombination  Attribute [no line break] Attributes
Attribute 
|  true
|  false

Validation

Validate[Attributes] (cxtContextenvEnvironment) propagates the call to Validate to nonterminals in the expansion of Attributes.
Validate[AttributeCombination] (cxtContextenvEnvironment) propagates the call to Validate to nonterminals in the expansion of AttributeCombination.
Validate[Attribute] (cxtContextenvEnvironment) propagates the call to Validate to nonterminals in the expansion of Attribute.

Setup

Setup[Attributes] () propagates the call to Setup to nonterminals in the expansion of Attributes.
Setup[AttributeCombination] () propagates the call to Setup to nonterminals in the expansion of AttributeCombination.
Setup[Attribute] () propagates the call to Setup to nonterminals in the expansion of Attribute.

Evaluation

proc Eval[Attributes] (envEnvironmentphasePhase): Attribute
[Attributes  Attributedo return Eval[Attribute](envphase);
[Attributes  AttributeCombinationdo return Eval[AttributeCombination](envphase)
end proc;
proc Eval[AttributeCombination  Attribute [no line break] Attributes] (envEnvironmentphasePhase): Attribute
aAttribute  Eval[Attribute](envphase);
if a = false then return false end if;
bAttribute  Eval[Attributes](envphase);
return combineAttributes(ab)
end proc;
proc Eval[Attribute] (envEnvironmentphasePhase): Attribute
[Attribute  AttributeExpressiondo
aObject  readReference(Eval[AttributeExpression](envphase), phase);
return objectToAttribute(aphase);
[Attribute  truedo return true;
[Attribute  falsedo return false;
[Attribute  ReservedNamespacedo return Eval[ReservedNamespace](envphase)
end proc;

Use Directive

Syntax

UseDirective  use namespace ParenListExpression

Validation

proc Validate[UseDirective  use namespace ParenListExpression] (cxtContextenvEnvironment)
Evaluate Validate[ParenListExpression](cxtenv) and ignore its result;
Evaluate Setup[ParenListExpression]() and ignore its result;
valuesObject[]  EvalAsList[ParenListExpression](envcompile);
namespacesNamespace{}  {};
for each v  values do
if v  Namespace then throw a TypeError exception end if;
namespaces  namespaces  {v}
end for each;
cxt.openNamespaces  cxt.openNamespaces  namespaces
end proc;

Import Directive

Syntax

ImportDirective 
   import PackageName
|  import Identifier = PackageName

Validation

proc Validate[ImportDirective] (cxtContextenvEnvironmentpreinstBooleanattrAttributeOptNotFalse)
[ImportDirective  import PackageNamedo
if not preinst then
throw a SyntaxError exception — a package may be imported only in a preinstantiated scope
end if;
frameFrame  env[0];
if frame  Package then
throw a SyntaxError exception — a package may be imported only into a package scope
end if;
if attr  {nonetruethen
throw an AttributeError exception — an unnamed import directive only permits the attributes true and false
end if;
pkgNameString  Name[PackageName];
pkgPackage  locatePackage(pkgName);
Evaluate importPackageInto(pkgframe) and ignore its result;
[ImportDirective  import Identifier = PackageNamedo
if not preinst then
throw a SyntaxError exception — a package may be imported only in a preinstantiated scope
end if;
frameFrame  env[0];
if frame  Package then
throw a SyntaxError exception — a package may be imported only into a package scope
end if;
aCompoundAttribute  toCompoundAttribute(attr);
if a.dynamic then
throw an AttributeError exception — a package definition cannot have the dynamic attribute
end if;
if a.prototype then
throw an AttributeError exception — a package definition cannot have the prototype attribute
end if;
pkgNameString  Name[PackageName];
pkgPackage  locatePackage(pkgName);
vVariable  new VariabletypePackage, valuepkg, immutabletrue, setupnone, initializernone;
Evaluate defineSingletonProperty(env, Name[Identifier], a.namespaces, a.overrideMod, a.explicit, readWrite, v) and ignore its result;
Evaluate importPackageInto(pkgframe) and ignore its result
end proc;
proc locatePackage(nameString): Package
Look for a package bound to name in the implementation’s list of available packages. If one is found, let pkgPackage be that package; otherwise, throw an implementation-defined error.
initialize: (()  ())  {nonebusy pkg.initialize;
case initialize of
{nonedo nothing;
{busydo throw an UninitializedError exception — circular package dependency;
()  () do
Evaluate initialize() and ignore its result;
note  pkg.initialize = none;
end case;
return pkg
end proc;
proc importPackageInto(sourcePackagedestinationPackage)
for each b  source.localBindings do
if not (b.explicit or b.content = forbidden or (some d  destination.localBindings satisfies b.qname = d.qname and accessesOverlap(b.accessesd.accesses))) then
destination.localBindings  destination.localBindings  {b}
end if
end for each
end proc;

Pragma

Syntax

Pragma  use PragmaItems
PragmaItems 
PragmaItem 
|  PragmaExpr ?
PragmaExpr 
PragmaArgument 
   true
|  false
|  Number
|  - Number
|  - NegatedMinLong
|  String

Validation

Validate[Pragma] (cxtContext) propagates the call to Validate to nonterminals in the expansion of Pragma.
Validate[PragmaItems] (cxtContext) propagates the call to Validate to nonterminals in the expansion of PragmaItems.
proc Validate[PragmaItem] (cxtContext)
[PragmaItem  PragmaExprdo
Evaluate Validate[PragmaExpr](cxtfalse) and ignore its result;
[PragmaItem  PragmaExpr ?do
Evaluate Validate[PragmaExpr](cxttrue) and ignore its result
end proc;
proc Validate[PragmaExpr] (cxtContextoptionalBoolean)
[PragmaExpr  Identifierdo
Evaluate processPragma(cxtName[Identifier], undefinedoptional) and ignore its result;
[PragmaExpr  Identifier ( PragmaArgument )do
argObject  Value[PragmaArgument];
Evaluate processPragma(cxtName[Identifier], argoptional) and ignore its result
end proc;
Value[PragmaArgument  true] = true;
Value[PragmaArgument  false] = false;
Value[PragmaArgument  Number] = Value[Number];
Value[PragmaArgument  - Number] = generalNumberNegate(Value[Number]);
Value[PragmaArgument  - NegatedMinLong] = (–263)long;
Value[PragmaArgument  String] = Value[String];
proc processPragma(cxtContextnameStringvalueObjectoptionalBoolean)
if name = “strict” then
if value  {trueundefinedthen cxt.strict  truereturn end if;
if value = false then cxt.strict  falsereturn end if
end if;
if name = “ecmascript” then
if value  {undefined, 4f64then return end if;
if value  {1f64, 2f64, 3f64then
An implementation may optionally modify cxt to disable features not available in ECMAScript Edition value other than subsequent pragmas.
return
end if
end if;
if not optional then throw a SyntaxError exception end if
end proc;

Definitions

Variable Definition

Syntax

VariableDefinition  VariableDefinitionKind VariableBindingList
VariableDefinitionKind 
   var
|  const
VariableBindingList 
   VariableBinding
|  VariableBindingList , VariableBinding
VariableBinding  TypedIdentifier VariableInitialisation
VariableInitialisation 
   «empty»
|  = VariableInitializer
VariableInitializer 
   AssignmentExpression
TypedIdentifier 
|  Identifier : TypeExpression

Validation

proc Validate[VariableDefinition  VariableDefinitionKind VariableBindingList] (cxtContextenvEnvironmentattrAttributeOptNotFalse)
Evaluate Validate[VariableBindingList](cxt, env, attr, Immutable[VariableDefinitionKind], false) and ignore its result
end proc;
Immutable[VariableDefinitionKind  var] = false;
Immutable[VariableDefinitionKind  const] = true;
Validate[VariableBindingList] (cxtContext, envEnvironment, attrAttributeOptNotFalse, immutableBoolean, noInitializerBoolean) propagates the call to Validate to nonterminals in the expansion of VariableBindingList.
CompileEnv[VariableBinding]: Environment;
CompileVar[VariableBinding]: Variable  DynamicVar  InstanceVariable;
OverriddenVar[VariableBinding]: InstanceVariableOpt;
Multiname[VariableBinding]: Multiname;
proc Validate[VariableBinding  TypedIdentifier VariableInitialisation] (cxtContext, envEnvironment, attrAttributeOptNotFalse, immutableBoolean, noInitializerBoolean)
Evaluate Validate[TypedIdentifier](cxtenv) and ignore its result;
Evaluate Validate[VariableInitialisation](cxtenv) and ignore its result;
CompileEnv[VariableBinding env;
nameString  Name[TypedIdentifier];
if not cxt.strict and getRegionalFrame(env Package  ParameterFrame and not immutable and attr = none and Plain[TypedIdentifierthen
qnameQualifiedName  public::name;
Multiname[VariableBinding {qname};
CompileVar[VariableBinding defineHoistedVar(envnameundefined)
else
aCompoundAttribute  toCompoundAttribute(attr);
if a.dynamic then
throw an AttributeError exception — a variable definition cannot have the dynamic attribute
end if;
if a.prototype then
throw an AttributeError exception — a variable definition cannot have the prototype attribute
end if;
categoryPropertyCategory  a.category;
if env[0]  Class then if category = none then category  final end if
else
if category  none then
throw an AttributeError exception — non-class variables cannot have a static, virtual, or final attribute
end if
end if;
case category of
{nonestaticdo
initializerInitializerOpt  Initializer[VariableInitialisation];
if noInitializer and initializer  none then
throw a SyntaxError exception — a for-in statement’s variable definition must not have an initialiser
end if;
proc variableSetup(): ClassOpt
typeClassOpt  SetupAndEval[TypedIdentifier](env);
Evaluate Setup[VariableInitialisation]() and ignore its result;
return type
end proc;
vVariable  new Variablevaluenone, immutableimmutable, setupvariableSetup, initializerinitializer, initializerEnvenv;
multinameMultiname  defineSingletonProperty(env, name, a.namespaces, a.overrideMod, a.explicit, readWrite, v);
Multiname[VariableBinding multiname;
CompileVar[VariableBinding v;
{virtualfinaldo
note  not noInitializer;
cClass  env[0];
vInstanceVariable  new InstanceVariablefinalcategory = finalimmutableimmutable;
vOverriddenInstanceVariableOpt  defineInstanceProperty(c, cxt, name, a.namespaces, a.overrideMod, a.explicit, v);
enumerableBoolean  a.enumerable;
if vOverridden  none and vOverridden.enumerable then enumerable  true
end if;
v.enumerable  enumerable;
OverriddenVar[VariableBinding vOverridden;
CompileVar[VariableBinding v
end case
end if
end proc;
Validate[VariableInitialisation] (cxtContextenvEnvironment) propagates the call to Validate to nonterminals in the expansion of VariableInitialisation.
Validate[VariableInitializer] (cxtContextenvEnvironment) propagates the call to Validate to nonterminals in the expansion of VariableInitializer.
Name[TypedIdentifier]: String;
Name[TypedIdentifier  Identifier] = Name[Identifier];
Name[TypedIdentifier  Identifier : TypeExpression] = Name[Identifier];
Plain[TypedIdentifier]: Boolean;
Plain[TypedIdentifier  Identifier] = true;
Plain[TypedIdentifier  Identifier : TypeExpression] = false;
proc Validate[TypedIdentifier] (cxtContextenvEnvironment)
[TypedIdentifier  Identifierdo nothing;
[TypedIdentifier  Identifier : TypeExpressiondo
Evaluate Validate[TypeExpression](cxtenv) and ignore its result
end proc;

Setup

proc Setup[VariableDefinition  VariableDefinitionKind VariableBindingList] ()
Evaluate Setup[VariableBindingList]() and ignore its result
end proc;
Setup[VariableBindingList] () propagates the call to Setup to nonterminals in the expansion of VariableBindingList.
proc Setup[VariableBinding  TypedIdentifier VariableInitialisation] ()
envEnvironment  CompileEnv[VariableBinding];
vVariable  DynamicVar  InstanceVariable  CompileVar[VariableBinding];
case v of
Evaluate setupVariable(v) and ignore its result;
if not v.immutable then
defaultValueObjectOpt  v.type.defaultValue;
if defaultValue = none then
throw an UninitializedError exception — Cannot declare a mutable variable of type Never
end if;
v.value  defaultValue
end if;
DynamicVar do Evaluate Setup[VariableInitialisation]() and ignore its result;
tClassOpt  SetupAndEval[TypedIdentifier](env);
if t = none then
overriddenVarInstanceVariableOpt  OverriddenVar[VariableBinding];
if overriddenVar  none then t  overriddenVar.type
else t  Object
end if
end if;
v.type  t;
Evaluate Setup[VariableInitialisation]() and ignore its result;
initializerInitializerOpt  Initializer[VariableInitialisation];
defaultValueObjectOpt  none;
if initializer  none then defaultValue  initializer(envcompile)
elsif not v.immutable then
defaultValue  t.defaultValue;
if defaultValue = none then
throw an UninitializedError exception — Cannot declare a mutable instance variable of type Never
end if
end if;
v.defaultValue  defaultValue
end case
end proc;
Setup[VariableInitialisation] () propagates the call to Setup to nonterminals in the expansion of VariableInitialisation.
Setup[VariableInitializer] () propagates the call to Setup to nonterminals in the expansion of VariableInitializer.

Evaluation

proc Eval[VariableDefinition  VariableDefinitionKind VariableBindingList] (envEnvironmentdObject): Object
Evaluate Eval[VariableBindingList](env) and ignore its result;
return d
end proc;
Eval[VariableBindingList] (envEnvironment) propagates the call to Eval to nonterminals in the expansion of VariableBindingList.
proc Eval[VariableBinding  TypedIdentifier VariableInitialisation] (envEnvironment)
case CompileVar[VariableBindingof
innerFrameNonWithFrame  env[0];
propertiesSingletonProperty{}  {b.content | b  innerFrame.localBindings such that b.qname  Multiname[VariableBinding]};
note  The properties set consists of exactly one Variable element because innerFrame was constructed with that Variable inside Validate.
vVariable  the one element of properties;
initializerInitializer  {nonebusy v.initializer;
case initializer of
{nonedo nothing;
{busydo throw a ReferenceError exception;
v.initializer  busy;
valueObject  initializer(v.initializerEnvrun);
Evaluate writeVariable(vvaluetrue) and ignore its result
end case;
initializerInitializerOpt  Initializer[VariableInitialisation];
if initializer  none then
valueObject  initializer(envrun);
Evaluate lexicalWrite(envMultiname[VariableBinding], valuefalserun) and ignore its result
end if;
InstanceVariable do nothing
end case
end proc;
proc WriteBinding[VariableBinding  TypedIdentifier VariableInitialisation] (envEnvironmentnewValueObject)
case CompileVar[VariableBindingof
innerFrameNonWithFrame  env[0];
propertiesSingletonProperty{}  {b.content | b  innerFrame.localBindings such that b.qname  Multiname[VariableBinding]};
note  The properties set consists of exactly one Variable element because innerFrame was constructed with that Variable inside Validate.
vVariable  the one element of properties;
Evaluate writeVariable(vnewValuefalse) and ignore its result;
Evaluate lexicalWrite(envMultiname[VariableBinding], newValuefalserun) and ignore its result
end case
end proc;
Initializer[VariableInitialisation]: InitializerOpt;
Initializer[VariableInitialisation  «empty»] = none;
Initializer[VariableInitialisation  = VariableInitializer] = Eval[VariableInitializer];
proc Eval[VariableInitializer] (envEnvironmentphasePhase): Object
[VariableInitializer  AssignmentExpressiondo
return readReference(Eval[AssignmentExpression](envphase), phase);
[VariableInitializer  AttributeCombinationdo
return Eval[AttributeCombination](envphase)
end proc;
proc SetupAndEval[TypedIdentifier] (envEnvironment): ClassOpt
[TypedIdentifier  Identifierdo return none;
[TypedIdentifier  Identifier : TypeExpressiondo
return SetupAndEval[TypeExpression](env)
end proc;

Simple Variable Definition

Syntax

A SimpleVariableDefinition represents the subset of VariableDefinition expansions that may be used when the variable definition is used as a Substatement instead of a Directive in non-strict mode. In strict mode variable definitions may not be used as substatements.

SimpleVariableDefinition  var UntypedVariableBindingList
UntypedVariableBindingList 
UntypedVariableBinding  Identifier VariableInitialisationallowIn

Validation

proc Validate[SimpleVariableDefinition  var UntypedVariableBindingList] (cxtContextenvEnvironment)
if cxt.strict or getRegionalFrame(env Package  ParameterFrame then
throw a SyntaxError exception — a variable may not be defined in a substatement except inside a non-strict function or non-strict top-level code; to fix this error, place the definition inside a block
end if;
Evaluate Validate[UntypedVariableBindingList](cxtenv) and ignore its result
end proc;
Validate[UntypedVariableBindingList] (cxtContextenvEnvironment) propagates the call to Validate to nonterminals in the expansion of UntypedVariableBindingList.
proc Validate[UntypedVariableBinding  Identifier VariableInitialisationallowIn] (cxtContextenvEnvironment)
Evaluate Validate[VariableInitialisationallowIn](cxtenv) and ignore its result;
Evaluate defineHoistedVar(envName[Identifier], undefined) and ignore its result
end proc;

Setup

Setup[SimpleVariableDefinition] () propagates the call to Setup to nonterminals in the expansion of SimpleVariableDefinition.
Setup[UntypedVariableBindingList] () propagates the call to Setup to nonterminals in the expansion of UntypedVariableBindingList.
proc Setup[UntypedVariableBinding  Identifier VariableInitialisationallowIn] ()
Evaluate Setup[VariableInitialisationallowIn]() and ignore its result
end proc;

Evaluation

proc Eval[SimpleVariableDefinition  var UntypedVariableBindingList] (envEnvironmentdObject): Object
Evaluate Eval[UntypedVariableBindingList](env) and ignore its result;
return d
end proc;
Eval[UntypedVariableBindingList] (envEnvironment) propagates the call to Eval to nonterminals in the expansion of UntypedVariableBindingList.
proc Eval[UntypedVariableBinding  Identifier VariableInitialisationallowIn] (envEnvironment)
initializerInitializerOpt  Initializer[VariableInitialisationallowIn];
if initializer  none then
valueObject  initializer(envrun);
qnameQualifiedName  public::(Name[Identifier]);
Evaluate lexicalWrite(env, {qname}, valuefalserun) and ignore its result
end if
end proc;

Function Definition

Syntax

FunctionDefinition  function FunctionName FunctionCommon
FunctionName 
|  get [no line break] Identifier
|  set [no line break] Identifier
FunctionCommon  ( Parameters ) Result Block

Validation

proc ValidateStatic[FunctionDefinition  function FunctionName FunctionCommon] (cxtContext, envEnvironment, preinstBoolean, aCompoundAttribute, uncheckedBoolean, hoistedBoolean)
nameString  Name[FunctionName];
handlingHandling  Handling[FunctionName];
case handling of
{normaldo
if unchecked then kind  uncheckedFunction
elsif a.prototype then kind  prototypeFunction
else kind  plainFunction
end if;
fSimpleInstance  UninstantiatedFunction  ValidateStaticFunction[FunctionCommon](cxtenvkind);
if preinst then f  instantiateFunction(fenvend if;
if hoisted then Evaluate defineHoistedVar(envnamef) and ignore its result
else
vVariable  new VariabletypeFunction, valuef, immutabletrue, setupnone, initializernone;
Evaluate defineSingletonProperty(env, name, a.namespaces, a.overrideMod, a.explicit, readWrite, v) and ignore its result
end if;
{getsetdo
if a.prototype then
throw an AttributeError exception — a getter or setter cannot have the prototype attribute
end if;
note  not (unchecked or hoisted);
Evaluate Validate[FunctionCommon](cxtenvplainFunctionhandling) and ignore its result;
boundEnvEnvironmentOpt  none;
if preinst then boundEnv  env end if;
case handling of
{getdo
getterGetter  new GettercallEvalStaticGet[FunctionCommon], envboundEnv;
Evaluate defineSingletonProperty(env, name, a.namespaces, a.overrideMod, a.explicit, read, getter) and ignore its result;
{setdo
setterSetter  new SettercallEvalStaticSet[FunctionCommon], envboundEnv;
Evaluate defineSingletonProperty(env, name, a.namespaces, a.overrideMod, a.explicit, write, setter) and ignore its result
end case
end case;
OverriddenProperty[FunctionDefinition none
end proc;
proc ValidateInstance[FunctionDefinition  function FunctionName FunctionCommon] (cxtContextenvEnvironmentcClassaCompoundAttributefinalBoolean)
if a.prototype then
throw an AttributeError exception — an instance method cannot have the prototype attribute
end if;
handlingHandling  Handling[FunctionName];
Evaluate Validate[FunctionCommon](cxtenvinstanceFunctionhandling) and ignore its result;
signatureParameterFrame  CompileFrame[FunctionCommon];
case handling of
{normaldo
m  new InstanceMethodfinalfinal, signaturesignature, lengthsignatureLength(signature), callEvalInstanceCall[FunctionCommon];
{getdo
m  new InstanceGetterfinalfinal, signaturesignature, callEvalInstanceGet[FunctionCommon];
{setdo
m  new InstanceSetterfinalfinal, signaturesignature, callEvalInstanceSet[FunctionCommon]
end case;
mOverriddenInstancePropertyOpt  defineInstanceProperty(c, cxt, Name[FunctionName], a.namespaces, a.overrideMod, a.explicit, m);
enumerableBoolean  a.enumerable;
if mOverridden  none and mOverridden.enumerable then enumerable  true end if;
m.enumerable  enumerable;
OverriddenProperty[FunctionDefinition mOverridden
end proc;
proc ValidateConstructor[FunctionDefinition  function FunctionName FunctionCommon] (cxtContextenvEnvironmentcClassaCompoundAttribute)
if a.prototype then
throw an AttributeError exception — a class constructor cannot have the prototype attribute
end if;
if Handling[FunctionName {getsetthen
throw a SyntaxError exception — a class constructor cannot be a getter or a setter
end if;
Evaluate Validate[FunctionCommon](cxtenvconstructorFunctionnormal) and ignore its result;
if c.init  none then
throw a DefinitionError exception — duplicate constructor definition
end if;
c.init  EvalInstanceInit[FunctionCommon];
OverriddenProperty[FunctionDefinition none
end proc;
proc Validate[FunctionDefinition  function FunctionName FunctionCommon] (cxtContextenvEnvironmentpreinstBooleanattrAttributeOptNotFalse)
aCompoundAttribute  toCompoundAttribute(attr);
if a.dynamic then
throw an AttributeError exception — a function cannot have the dynamic attribute
end if;
frameFrame  env[0];
if frame  Class then
note  preinst;
case a.category of
{staticdo
Evaluate ValidateStatic[FunctionDefinition](cxtenvpreinstafalsefalse) and ignore its result;
{nonedo
if Name[FunctionName] = frame.name then
Evaluate ValidateConstructor[FunctionDefinition](cxtenvframea) and ignore its result
else
Evaluate ValidateInstance[FunctionDefinition](cxtenvframeafalse) and ignore its result
end if;
{virtualdo
Evaluate ValidateInstance[FunctionDefinition](cxtenvframeafalse) and ignore its result;
{finaldo
Evaluate ValidateInstance[FunctionDefinition](cxtenvframeatrue) and ignore its result
end case
else
if a.category  none then
throw an AttributeError exception — non-class functions cannot have a static, virtual, or final attribute
end if;
uncheckedBoolean  not cxt.strict and Handling[FunctionName] = normal and Plain[FunctionCommon];
hoistedBoolean  unchecked and attr = none and (frame  Package or (frame  LocalFrame and env[1]  ParameterFrame));
Evaluate ValidateStatic[FunctionDefinition](cxtenvpreinstauncheckedhoisted) and ignore its result
end if
end proc;
Handling[FunctionName]: Handling;
Handling[FunctionName  Identifier] = normal;
Handling[FunctionName  get [no line break] Identifier] = get;
Handling[FunctionName  set [no line break] Identifier] = set;
Name[FunctionName  Identifier] = Name[Identifier];
Name[FunctionName  get [no line break] Identifier] = Name[Identifier];
Name[FunctionName  set [no line break] Identifier] = Name[Identifier];
Plain[FunctionCommon  ( Parameters ) Result Block]: BooleanPlain[Parametersand Plain[Result];
proc Validate[FunctionCommon  ( Parameters ) Result Block] (cxtContextenvEnvironmentkindFunctionKindhandlingHandling)
localCxtContext  new Contextstrictcxt.strictopenNamespacescxt.openNamespaces;
superconstructorCalledBoolean  kind  constructorFunction;
compileFrameParameterFrame  new ParameterFramelocalBindings: {}, kindkind, handlinghandling, callsSuperconstructorfalse, superconstructorCalledsuperconstructorCalled, thisnone, parameters[], restnone;
compileEnvEnvironment  [compileFrame]  env;
CompileFrame[FunctionCommon compileFrame;
CompileEnv[FunctionCommon compileEnv;
if kind = uncheckedFunction then
Evaluate defineHoistedVar(compileEnv, “arguments”, undefined) and ignore its result
end if;
Evaluate Validate[Parameters](localCxtcompileEnvcompileFrame) and ignore its result;
Evaluate Validate[Result](localCxtcompileEnv) and ignore its result;
Evaluate Validate[Block](localCxt, compileEnv, JumpTargetsbreakTargets: {}, continueTargets: {}, false) and ignore its result
end proc;
proc ValidateStaticFunction[FunctionCommon  ( Parameters ) Result Block] (cxtContextenvEnvironmentkindStaticFunctionKind): UninstantiatedFunction
Evaluate Validate[FunctionCommon](cxtenvkindnormal) and ignore its result;
lengthInteger  ParameterCount[Parameters];
case kind of
return new UninstantiatedFunctiontypeFunction, lengthlength, callEvalStaticCall[FunctionCommon], constructnone, instantiations: {};
return new UninstantiatedFunctiontypePrototypeFunction, lengthlength, callEvalStaticCall[FunctionCommon], constructEvalPrototypeConstruct[FunctionCommon], instantiations: {}
end case
end proc;

Setup

proc Setup[FunctionDefinition  function FunctionName FunctionCommon] ()
overriddenPropertyInstancePropertyOpt  OverriddenProperty[FunctionDefinition];
case overriddenProperty of
{nonedo Evaluate Setup[FunctionCommon]() and ignore its result;
InstanceMethod  InstanceGetter  InstanceSetter do
Evaluate SetupOverride[FunctionCommon](overriddenProperty.signature) and ignore its result;
overriddenSignatureParameterFrame;
case Handling[FunctionNameof
{normaldo
This cannot happen because ValidateInstance already ensured that a function cannot override an instance variable.
{getdo
overriddenSignature  new ParameterFramelocalBindings: {}, kindinstanceFunction, handlingget, callsSuperconstructorfalse, superconstructorCalledfalse, thisnone, parameters[], restnone, returnTypeoverriddenProperty.type;
{setdo
vVariable  new VariabletypeoverriddenProperty.type, valuenone, immutablefalse, setupnone, initializernone;
parametersParameter[]  [Parametervarvdefaultnone];
overriddenSignature  new ParameterFramelocalBindings: {}, kindinstanceFunction, handlingset, callsSuperconstructorfalse, superconstructorCalledfalse, thisnone, parametersparameters, restnone, returnTypeVoid
end case;
Evaluate SetupOverride[FunctionCommon](overriddenSignature) and ignore its result
end case
end proc;
proc Setup[FunctionCommon  ( Parameters ) Result Block] ()
compileEnvEnvironment  CompileEnv[FunctionCommon];
compileFrameParameterFrame  CompileFrame[FunctionCommon];
Evaluate Setup[Parameters](compileEnvcompileFrame) and ignore its result;
Evaluate checkAccessorParameters(compileFrame) and ignore its result;
Evaluate Setup[Result](compileEnvcompileFrame) and ignore its result;
Evaluate Setup[Block]() and ignore its result
end proc;
proc SetupOverride[FunctionCommon  ( Parameters ) Result Block] (overriddenSignatureParameterFrame)
compileEnvEnvironment  CompileEnv[FunctionCommon];
compileFrameParameterFrame  CompileFrame[FunctionCommon];
Evaluate SetupOverride[Parameters](compileEnvcompileFrameoverriddenSignature) and ignore its result;
Evaluate checkAccessorParameters(compileFrame) and ignore its result;
Evaluate SetupOverride[Result](compileEnvcompileFrameoverriddenSignature) and ignore its result;
Evaluate Setup[Block]() and ignore its result
end proc;

Evaluation

proc EvalStaticCall[FunctionCommon  ( Parameters ) Result Block] (thisObjectfSimpleInstanceargsObject[], phasePhase): Object
note  The check that phase compile also ensures that Setup has been called.
if phase = compile then
throw a ConstantError exception — a constant expression cannot call user-defined functions
end if;
runtimeEnvEnvironment  f.env;
runtimeThisObjectOpt  none;
compileFrameParameterFrame  CompileFrame[FunctionCommon];
if compileFrame.kind  {uncheckedFunctionprototypeFunctionthen
if this  PrimitiveObject then runtimeThis  getPackageFrame(runtimeEnv)
else runtimeThis  this
end if
end if;
runtimeFrameParameterFrame  instantiateParameterFrame(compileFrameruntimeEnvruntimeThis);
Evaluate assignArguments(runtimeFramefargsphase) and ignore its result;
resultObject;
try
Evaluate Eval[Block]([runtimeFrame]  runtimeEnvundefined) and ignore its result;
result  undefined
catch xSemanticException do
if x  Return then result  x.value else throw x end if
end try;
return coerce(resultruntimeFrame.returnType)
end proc;
proc EvalStaticGet[FunctionCommon  ( Parameters ) Result Block] (runtimeEnvEnvironmentphasePhase): Object
note  The check that phase compile also ensures that Setup has been called.
if phase = compile then
throw a ConstantError exception — a constant expression cannot call user-defined getters
end if;
compileFrameParameterFrame  CompileFrame[FunctionCommon];
runtimeFrameParameterFrame  instantiateParameterFrame(compileFrameruntimeEnvnone);
Evaluate assignArguments(runtimeFramenone[]phase) and ignore its result;
resultObject;
try
Evaluate Eval[Block]([runtimeFrame]  runtimeEnvundefined) and ignore its result;
throw a SyntaxError exception — a getter must return a value and may not return by falling off the end of its code
catch xSemanticException do
if x  Return then result  x.value else throw x end if
end try;
return coerce(resultruntimeFrame.returnType)
end proc;
proc EvalStaticSet[FunctionCommon  ( Parameters ) Result Block] (newValueObjectruntimeEnvEnvironmentphasePhase)
note  The check that phase compile also ensures that Setup has been called.
if phase = compile then
throw a ConstantError exception — a constant expression cannot call setters
end if;
compileFrameParameterFrame  CompileFrame[FunctionCommon];
runtimeFrameParameterFrame  instantiateParameterFrame(compileFrameruntimeEnvnone);
Evaluate assignArguments(runtimeFramenone[newValue]phase) and ignore its result;
try
Evaluate Eval[Block]([runtimeFrame]  runtimeEnvundefined) and ignore its result
catch xSemanticException do if x  Return then throw x end if
end try
end proc;
proc EvalInstanceCall[FunctionCommon  ( Parameters ) Result Block] (thisObjectargsObject[], phasePhase): Object
note  The check that phase compile also ensures that Setup has been called.
if phase = compile then
throw a ConstantError exception — a constant expression cannot call user-defined functions
end if;
note  Class frames are always preinstantiated, so the run environment is the same as compile environment.
envEnvironment  CompileEnv[FunctionCommon];
compileFrameParameterFrame  CompileFrame[FunctionCommon];
runtimeFrameParameterFrame  instantiateParameterFrame(compileFrameenvthis);
Evaluate assignArguments(runtimeFramenoneargsphase) and ignore its result;
resultObject;
try
Evaluate Eval[Block]([runtimeFrame]  envundefined) and ignore its result;
result  undefined
catch xSemanticException do
if x  Return then result  x.value else throw x end if
end try;
return coerce(resultruntimeFrame.returnType)
end proc;
proc EvalInstanceGet[FunctionCommon  ( Parameters ) Result Block] (thisObjectphasePhase): Object
note  The check that phase compile also ensures that Setup has been called.
if phase = compile then
throw a ConstantError exception — a constant expression cannot call user-defined getters
end if;
note  Class frames are always preinstantiated, so the run environment is the same as compile environment.
envEnvironment  CompileEnv[FunctionCommon];
compileFrameParameterFrame  CompileFrame[FunctionCommon];
runtimeFrameParameterFrame  instantiateParameterFrame(compileFrameenvthis);
Evaluate assignArguments(runtimeFramenone[]phase) and ignore its result;
resultObject;
try
Evaluate Eval[Block]([runtimeFrame]  envundefined) and ignore its result;
throw a SyntaxError exception — a getter must return a value and may not return by falling off the end of its code
catch xSemanticException do
if x  Return then result  x.value else throw x end if
end try;
return coerce(resultruntimeFrame.returnType)
end proc;
proc EvalInstanceSet[FunctionCommon  ( Parameters ) Result Block] (thisObjectnewValueObjectphasePhase)
note  The check that phase compile also ensures that Setup has been called.
if phase = compile then
throw a ConstantError exception — a constant expression cannot call setters
end if;
note  Class frames are always preinstantiated, so the run environment is the same as compile environment.
envEnvironment  CompileEnv[FunctionCommon];
compileFrameParameterFrame  CompileFrame[FunctionCommon];
runtimeFrameParameterFrame  instantiateParameterFrame(compileFrameenvthis);
Evaluate assignArguments(runtimeFramenone[newValue]phase) and ignore its result;
try Evaluate Eval[Block]([runtimeFrame]  envundefined) and ignore its result
catch xSemanticException do if x  Return then throw x end if
end try
end proc;
proc EvalInstanceInit[FunctionCommon  ( Parameters ) Result Block] (thisSimpleInstanceargsObject[], phase: {run})
note  Class frames are always preinstantiated, so the run environment is the same as compile environment.
envEnvironment  CompileEnv[FunctionCommon];
compileFrameParameterFrame  CompileFrame[FunctionCommon];
runtimeFrameParameterFrame  instantiateParameterFrame(compileFrameenvthis);
Evaluate assignArguments(runtimeFramenoneargsphase) and ignore its result;
if not runtimeFrame.callsSuperconstructor then
cClass  getEnclosingClass(env);
Evaluate callInit(thisc.super[]run) and ignore its result;
runtimeFrame.superconstructorCalled  true
end if;
try Evaluate Eval[Block]([runtimeFrame]  envundefined) and ignore its result
catch xSemanticException do if x  Return then throw x end if
end try;
if not runtimeFrame.superconstructorCalled then
throw an UninitializedError exception — the superconstructor must be called before returning normally from a constructor
end if
end proc;
proc EvalPrototypeConstruct[FunctionCommon  ( Parameters ) Result Block] (fSimpleInstanceargsObject[], phasePhase): Object
note  The check that phase compile also ensures that Setup has been called.
if phase = compile then
throw a ConstantError exception — a constant expression cannot call user-defined prototype constructors
end if;
runtimeEnvEnvironment  f.env;
archetypeObject  dotRead(f, {public::“prototype”}, phase);
if archetype  {nullundefinedthen archetype  ObjectPrototype
elsif objectType(archetype Object then
throw a TypeError exception — bad prototype value
end if;
oObject  createSimpleInstance(Objectarchetypenonenonenone);
compileFrameParameterFrame  CompileFrame[FunctionCommon];
runtimeFrameParameterFrame  instantiateParameterFrame(compileFrameruntimeEnvo);
Evaluate assignArguments(runtimeFramefargsphase) and ignore its result;
resultObject;
try
Evaluate Eval[Block]([runtimeFrame]  runtimeEnvundefined) and ignore its result;
result  undefined
catch xSemanticException do
if x  Return then result  x.value else throw x end if
end try;
coercedResultObject  coerce(resultruntimeFrame.returnType);
if coercedResult  PrimitiveObject then return o else return coercedResult end if
end proc;
proc checkAccessorParameters(frameParameterFrame)
parametersParameter[]  frame.parameters;
restVariableOpt  frame.rest;
case frame.handling of
{normaldo nothing;
{getdo
if parameters  [] or rest  none then
throw a SyntaxError exception — a getter cannot take any parameters
end if;
{setdo
if |parameters 1 or rest  none then
throw a SyntaxError exception — a setter must take exactly one parameter
end if;
if parameters[0].default  none then
throw a SyntaxError exception — a setter’s parameter cannot be optional
end if
end case
end proc;
proc assignArguments(runtimeFrameParameterFrame, fSimpleInstance  {none}, argsObject[], phase: {run})
This procedure performs a number of checks on the arguments, including checking their count, names, and values. Although this procedure performs these checks in a specific order for expository purposes, an implementation may perform these checks in a different order, which could have the effect of reporting a different error if there are multiple errors. For example, if a function only allows between 2 and 4 arguments, the first of which must be a Number and is passed five arguments the first of which is a String, then the implementation may throw an exception either about the argument count mismatch or about the type coercion error in the first argument.
argumentsObjectObjectOpt  none;
if runtimeFrame.kind = uncheckedFunction then
argumentsObject  construct(Array[]phase);
Evaluate createDynamicProperty(argumentsObjectpublic::“callee”, falsefalsef) and ignore its result;
Evaluate writeArrayPrivateLength(argumentsObject, |args|, phase) and ignore its result
end if;
restObjectObjectOpt  none;
restVariable  {none runtimeFrame.rest;
if rest  none then restObject  construct(Array[]phaseend if;
parametersParameter[]  runtimeFrame.parameters;
iInteger  0;
jInteger  0;
for each arg  args do
if i < |parametersthen
parameterParameter  parameters[i];
defaultObjectOpt  parameter.default;
argOrDefaultObject  arg;
if argOrDefault = undefined and default  none then argOrDefault  default
end if;
vDynamicVar  Variable  parameter.var;
Evaluate writeSingletonProperty(vargOrDefaultphase) and ignore its result;
if argumentsObject  none then
note  Create an alias of v as the ith entry of the arguments object.
note  v  DynamicVar;
qnameQualifiedName  objectToQualifiedName(if64phase);
argumentsObject.localBindings  argumentsObject.localBindings  {LocalBindingqnameqname, accessesreadWrite, explicitfalse, enumerablefalse, contentv}
end if
elsif restObject  none then
if j  arrayLimit then throw a RangeError exception end if;
Evaluate indexWrite(restObjectjargphase) and ignore its result;
note  argumentsObject = none because a function can't have both a rest parameter and an arguments object.
j  j + 1
elsif argumentsObject  none then
Evaluate indexWrite(argumentsObjectiargphase) and ignore its result
else
throw an ArgumentError exception — more arguments than parameters were supplied, and the called function does not have a ... parameter and is not unchecked.
end if;
i  i + 1
end for each;
while i < |parametersdo
parameterParameter  parameters[i];
defaultObjectOpt  parameter.default;
if default = none then
if argumentsObject  none then default  undefined
else
throw an ArgumentError exception — fewer arguments than parameters were supplied, and the called function does not supply default values for the missing parameters and is not unchecked.
end if
end if;
Evaluate writeSingletonProperty(parameter.vardefaultphase) and ignore its result;
i  i + 1
end while
end proc;
proc signatureLength(signatureParameterFrame): Integer
return |signature.parameters|
end proc;

Syntax

Parameters 
   «empty»
NonemptyParameters 
Parameter  ParameterAttributes TypedIdentifierallowIn
ParameterAttributes 
   «empty»
|  const
ParameterInit 
   Parameter
RestParameter 
   ...
Result 
   «empty»
|  : TypeExpressionallowIn

Validation

Plain[Parameters  «empty»] = true;
Plain[Parameters  NonemptyParameters] = Plain[NonemptyParameters];
ParameterCount[Parameters]: Integer;
ParameterCount[Parameters  «empty»] = 0;
ParameterCount[Parameters  NonemptyParameters] = ParameterCount[NonemptyParameters];
Validate[Parameters] (cxtContextenvEnvironmentcompileFrameParameterFrame) propagates the call to Validate to nonterminals in the expansion of Parameters.
Plain[NonemptyParameters  ParameterInit] = Plain[ParameterInit];
Plain[NonemptyParameters0  ParameterInit , NonemptyParameters1] = Plain[ParameterInitand Plain[NonemptyParameters1];
Plain[NonemptyParameters  RestParameter] = false;
ParameterCount[NonemptyParameters]: Integer;
ParameterCount[NonemptyParameters  ParameterInit] = 1;
ParameterCount[NonemptyParameters0  ParameterInit , NonemptyParameters1] = 1 + ParameterCount[NonemptyParameters1];
ParameterCount[NonemptyParameters  RestParameter] = 0;
Validate[NonemptyParameters] (cxtContextenvEnvironmentcompileFrameParameterFrame) propagates the call to Validate to nonterminals in the expansion of NonemptyParameters.
Name[Parameter  ParameterAttributes TypedIdentifierallowIn]: StringName[TypedIdentifierallowIn];
Plain[Parameter  ParameterAttributes TypedIdentifierallowIn]: BooleanPlain[TypedIdentifierallowInand not HasConst[ParameterAttributes];
CompileVar[Parameter]: DynamicVar  Variable;
proc Validate[Parameter  ParameterAttributes TypedIdentifierallowIn] (cxtContextenvEnvironmentcompileFrameParameterFrame  LocalFrame)
Evaluate Validate[TypedIdentifierallowIn](cxtenv) and ignore its result;
immutableBoolean  HasConst[ParameterAttributes];
nameString  Name[TypedIdentifierallowIn];
vDynamicVar  Variable;
if compileFrame  ParameterFrame and compileFrame.kind = uncheckedFunction then
note  not immutable;
v  defineHoistedVar(envnameundefined)
else
v  new Variablevaluenoneimmutableimmutablesetupnoneinitializernone;
Evaluate defineSingletonProperty(envname, {public}, nonefalsereadWritev) and ignore its result
end if;
CompileVar[Parameter v
end proc;
HasConst[ParameterAttributes  «empty»] = false;
HasConst[ParameterAttributes  const] = true;
Plain[ParameterInit  Parameter] = Plain[Parameter];
Plain[ParameterInit  Parameter = AssignmentExpressionallowIn] = false;
proc Validate[ParameterInit] (cxtContextenvEnvironmentcompileFrameParameterFrame)
[ParameterInit  Parameterdo
Evaluate Validate[Parameter](cxtenvcompileFrame) and ignore its result;
[ParameterInit  Parameter = AssignmentExpressionallowIndo
Evaluate Validate[Parameter](cxtenvcompileFrame) and ignore its result;
Evaluate Validate[AssignmentExpressionallowIn](cxtenv) and ignore its result
end proc;
proc Validate[RestParameter] (cxtContextenvEnvironmentcompileFrameParameterFrame)
[RestParameter  ...do
note  compileFrame.kind  uncheckedFunction;
vVariable  new VariabletypeArray, valuenone, immutabletrue, setupnone, initializernone;
compileFrame.rest  v;
[RestParameter  ... ParameterAttributes Identifierdo
note  compileFrame.kind  uncheckedFunction;
vVariable  new VariabletypeArray, valuenone, immutableHasConst[ParameterAttributes], setupnone, initializernone;
compileFrame.rest  v;
nameString  Name[Identifier];
Evaluate defineSingletonProperty(envname, {public}, nonefalsereadWritev) and ignore its result
end proc;
Plain[Result]: Boolean;
Plain[Result  «empty»] = true;
Plain[Result  : TypeExpressionallowIn] = false;
Validate[Result] (cxtContextenvEnvironment) propagates the call to Validate to nonterminals in the expansion of Result.

Setup

Setup[Parameters] (compileEnvEnvironmentcompileFrameParameterFrame) propagates the call to Setup to nonterminals in the expansion of Parameters.
proc SetupOverride[Parameters] (compileEnvEnvironment, compileFrameParameterFrame, overriddenSignatureParameterFrame)
[Parameters  «empty»] do
if overriddenSignature.parameters  [] or overriddenSignature.rest  none then
throw a DefinitionError exception — mismatch with the overridden method’s signature
end if;
[Parameters  NonemptyParametersdo
Evaluate SetupOverride[NonemptyParameters](compileEnv, compileFrame, overriddenSignature, overriddenSignature.parameters) and ignore its result
end proc;
proc Setup[NonemptyParameters] (compileEnvEnvironmentcompileFrameParameterFrame)
[NonemptyParameters  ParameterInitdo
Evaluate Setup[ParameterInit](compileEnvcompileFrame) and ignore its result;
[NonemptyParameters0  ParameterInit , NonemptyParameters1do
Evaluate Setup[ParameterInit](compileEnvcompileFrame) and ignore its result;
Evaluate Setup[NonemptyParameters1](compileEnvcompileFrame) and ignore its result;
[NonemptyParameters  RestParameterdo nothing
end proc;
proc SetupOverride[NonemptyParameters] (compileEnvEnvironment, compileFrameParameterFrame, overriddenSignatureParameterFrame, overriddenParametersParameter[])
[NonemptyParameters  ParameterInitdo
if overriddenParameters = [] then
throw a DefinitionError exception — mismatch with the overridden method’s signature
end if;
Evaluate SetupOverride[ParameterInit](compileEnvcompileFrameoverriddenParameters[0]) and ignore its result;
if |overriddenParameters 1 or overriddenSignature.rest  none then
throw a DefinitionError exception — mismatch with the overridden method’s signature
end if;
[NonemptyParameters0  ParameterInit , NonemptyParameters1do
if overriddenParameters = [] then
throw a DefinitionError exception — mismatch with the overridden method’s signature
end if;
Evaluate SetupOverride[ParameterInit](compileEnvcompileFrameoverriddenParameters[0]) and ignore its result;
Evaluate SetupOverride[NonemptyParameters1](compileEnv, compileFrame, overriddenSignature, overriddenParameters[1 ...]) and ignore its result;
[NonemptyParameters  RestParameterdo
if overriddenParameters  [] then
throw a DefinitionError exception — mismatch with the overridden method’s signature
end if;
overriddenRestVariable  {none overriddenSignature.rest;
if overriddenRest = none or overriddenRest.type  Array then
throw a DefinitionError exception — mismatch with the overridden method’s signature
end if
end proc;
proc Setup[Parameter  ParameterAttributes TypedIdentifierallowIn] (compileEnvEnvironmentcompileFrameParameterFrame  LocalFramedefaultObjectOpt)
if compileFrame  ParameterFrame and default = none and (some p2  compileFrame.parameters satisfies p2.default  nonethen
throw a SyntaxError exception — a required parameter cannot follow an optional one
end if;
vDynamicVar  Variable  CompileVar[Parameter];
case v of
DynamicVar do nothing;
typeClassOpt  SetupAndEval[TypedIdentifierallowIn](compileEnv);
if type = none then type  Object end if;
v.type  type
end case;
if compileFrame  ParameterFrame then
pParameter  Parametervarvdefaultdefault;
compileFrame.parameters  compileFrame.parameters  [p]
end if
end proc;
proc SetupOverride[Parameter  ParameterAttributes TypedIdentifierallowIn] (compileEnvEnvironment, compileFrameParameterFrame, defaultObjectOpt, overriddenParameterParameter)
newDefaultObjectOpt  default;
if newDefault = none then newDefault  overriddenParameter.default end if;
if default = none and (some p2  compileFrame.parameters satisfies p2.default  nonethen
throw a SyntaxError exception — a required parameter cannot follow an optional one
end if;
vDynamicVar  Variable  CompileVar[Parameter];
note  v  DynamicVar;
typeClassOpt  SetupAndEval[TypedIdentifierallowIn](compileEnv);
if type = none then type  Object end if;
if type  overriddenParameter.var.type then
throw a DefinitionError exception — mismatch with the overridden method’s signature
end if;
v.type  type;
pParameter  ParametervarvdefaultnewDefault;
compileFrame.parameters  compileFrame.parameters  [p]
end proc;
proc Setup[ParameterInit] (compileEnvEnvironmentcompileFrameParameterFrame)
[ParameterInit  Parameterdo
Evaluate Setup[Parameter](compileEnvcompileFramenone) and ignore its result;
[ParameterInit  Parameter = AssignmentExpressionallowIndo
Evaluate Setup[AssignmentExpressionallowIn]() and ignore its result;
defaultObject  readReference(Eval[AssignmentExpressionallowIn](compileEnvcompile), compile);
Evaluate Setup[Parameter](compileEnvcompileFramedefault) and ignore its result
end proc;
proc SetupOverride[ParameterInit] (compileEnvEnvironmentcompileFrameParameterFrameoverriddenParameterParameter)
[ParameterInit  Parameterdo
Evaluate SetupOverride[Parameter](compileEnvcompileFramenoneoverriddenParameter) and ignore its result;
[ParameterInit  Parameter = AssignmentExpressionallowIndo
Evaluate Setup[AssignmentExpressionallowIn]() and ignore its result;
defaultObject  readReference(Eval[AssignmentExpressionallowIn](compileEnvcompile), compile);
Evaluate SetupOverride[Parameter](compileEnvcompileFramedefaultoverriddenParameter) and ignore its result
end proc;
proc Setup[Result] (compileEnvEnvironmentcompileFrameParameterFrame)
[Result  «empty»] do
defaultReturnTypeClass  Object;
if cannotReturnValue(compileFramethen defaultReturnType  Void end if;
compileFrame.returnType  defaultReturnType;
[Result  : TypeExpressionallowIndo
if cannotReturnValue(compileFramethen
throw a SyntaxError exception — a setter or constructor cannot define a return type
end if;
compileFrame.returnType  SetupAndEval[TypeExpressionallowIn](compileEnv)
end proc;
proc SetupOverride[Result] (compileEnvEnvironment, compileFrameParameterFrame, overriddenSignatureParameterFrame)
[Result  «empty»] do compileFrame.returnType  overriddenSignature.returnType;
[Result  : TypeExpressionallowIndo
tClass  SetupAndEval[TypeExpressionallowIn](compileEnv);
if overriddenSignature.returnType  t then
throw a DefinitionError exception — mismatch with the overridden method’s signature
end if;
compileFrame.returnType  t
end proc;

Class Definition

Syntax

ClassDefinition  class Identifier Inheritance Block
Inheritance 
   «empty»
|  extends TypeExpressionallowIn

Validation

proc Validate[ClassDefinition  class Identifier Inheritance Block] (cxtContextenvEnvironmentpreinstBooleanattrAttributeOptNotFalse)
if not preinst then
throw a SyntaxError exception — a class may be defined only in a preinstantiated scope
end if;
superClass  Validate[Inheritance](cxtenv);
if not super.complete then
throw a ConstantError exception — cannot override a class before its definition has been compiled
end if;
if super.final then throw a DefinitionError exception — can’t override a final class
end if;
aCompoundAttribute  toCompoundAttribute(attr);
if a.prototype then
throw an AttributeError exception — a class definition cannot have the prototype attribute
end if;
finalBoolean;
case a.category of
{nonedo final  false;
{staticdo
if env[0]  Class then
throw an AttributeError exception — non-class property definitions cannot have a static attribute
end if;
final  false;
{finaldo final  true;
{virtualdo
throw an AttributeError exception — a class definition cannot have the virtual attribute
end case;
privateNamespaceNamespace  new Namespacename: “private;
dynamicBoolean  a.dynamic or (super.dynamic and super  Object);
cClass  new ClasslocalBindings: {}, instanceProperties: {}, supersuper, prototypesuper.prototype, completefalse, nameName[Identifier], typeofString: “object”, privateNamespaceprivateNamespace, dynamicdynamic, finalfinal, defaultValuenull, defaultHinthintNumber, hasPropertysuper.hasProperty, bracketReadsuper.bracketRead, bracketWritesuper.bracketWrite, bracketDeletesuper.bracketDelete, readsuper.read, writesuper.write, deletesuper.delete, enumeratesuper.enumerate, callordinaryCall, constructordinaryConstruct, initnone, isordinaryIs, coerceordinaryCoerce;
Class[ClassDefinition c;
vVariable  new VariabletypeClass, valuec, immutabletrue, setupnone, initializernone;
Evaluate defineSingletonProperty(env, Name[Identifier], a.namespaces, a.overrideMod, a.explicit, readWrite, v) and ignore its result;
innerCxtContext  new Contextstrictcxt.strict, openNamespacescxt.openNamespaces  {privateNamespace};
Evaluate ValidateUsingFrame[Block](innerCxt, env, JumpTargetsbreakTargets: {}, continueTargets: {}, preinst, c) and ignore its result;
if c.init = none then c.init  super.init end if;
c.complete  true
end proc;
proc Validate[Inheritance] (cxtContextenvEnvironment): Class
[Inheritance  «empty»] do return Object;
[Inheritance  extends TypeExpressionallowIndo
Evaluate Validate[TypeExpressionallowIn](cxtenv) and ignore its result;
return SetupAndEval[TypeExpressionallowIn](env)
end proc;

Setup

proc Setup[ClassDefinition  class Identifier Inheritance Block] ()
Evaluate Setup[Block]() and ignore its result
end proc;

Evaluation

proc Eval[ClassDefinition  class Identifier Inheritance Block] (envEnvironmentdObject): Object
cClass  Class[ClassDefinition];
return EvalUsingFrame[Block](envcd)
end proc;

Namespace Definition

Syntax

NamespaceDefinition  namespace Identifier

Validation

proc Validate[NamespaceDefinition  namespace Identifier] (cxtContextenvEnvironmentpreinstBooleanattrAttributeOptNotFalse)
if not preinst then
throw a SyntaxError exception — a namespace may be defined only in a preinstantiated scope
end if;
aCompoundAttribute  toCompoundAttribute(attr);
if a.dynamic then
throw an AttributeError exception — a namespace definition cannot have the dynamic attribute
end if;
if a.prototype then
throw an AttributeError exception — a namespace definition cannot have the prototype attribute
end if;
case a.category of
{nonedo nothing;
{staticdo
if env[0]  Class then
throw an AttributeError exception — non-class property definitions cannot have a static attribute
end if;
{virtualfinaldo
throw an AttributeError exception — a namespace definition cannot have the virtual or final attribute
end case;
nameString  Name[Identifier];
nsNamespace  new Namespacenamename;
vVariable  new VariabletypeNamespace, valuens, immutabletrue, setupnone, initializernone;
Evaluate defineSingletonProperty(env, name, a.namespaces, a.overrideMod, a.explicit, readWrite, v) and ignore its result
end proc;

Programs

Syntax

Program 

Processing

Process[Program]: Object;
Process[Program  Directives]
begin
cxtContext  new ContextstrictfalseopenNamespaces: {publicinternal};
initialEnvironmentEnvironment  [createGlobalObject()];
Evaluate Validate[Directives](cxt, initialEnvironment, JumpTargetsbreakTargets: {}, continueTargets: {}, true, none) and ignore its result;
Evaluate Setup[Directives]() and ignore its result;
return Eval[Directives](initialEnvironmentundefined)
end;
Process[Program0  PackageDefinition Program1]
begin
Evaluate Process[PackageDefinition] and ignore its result;
return Process[Program1]
end;

Package Definition

Syntax

PackageDefinition  package PackageNameOpt Block
PackageNameOpt 
   «empty»
PackageName 
   String
PackageIdentifiers 

Processing

Process[PackageDefinition  package PackageNameOpt Block]: Void
begin
nameString  Name[PackageNameOpt];
cxtContext  new ContextstrictfalseopenNamespaces: {publicinternal};
globalObjectPackage  createGlobalObject();
pkgInternalNamespace  new Namespacename: “internal;
pkgPackage  new PackagelocalBindings: {stdExplicitConstBinding(internal::“internal”, Namespaceinternal)}, archetypeObjectPrototype, namename, initializebusy, sealedtrue, internalNamespacepkgInternal;
initialEnvironmentEnvironment  [pkgglobalObject];
Evaluate Validate[Block](cxt, initialEnvironment, JumpTargetsbreakTargets: {}, continueTargets: {}, true) and ignore its result;
Evaluate Setup[Block]() and ignore its result;
proc evalPackage()
pkg.initialize  busy;
Evaluate Eval[Block](initialEnvironmentundefined) and ignore its result;
pkg.initialize  none
end proc;
pkg.initialize  evalPackage;
Bind name to package pkg in the system’s list of packages in an implementation-defined manner.
end;
Name[PackageNameOpt  «empty»] = an implementation-supplied name;
Name[PackageNameOpt  PackageName] = Name[PackageName];
Name[PackageName  String] = Value[String] processed in an implementation-defined manner;
Name[PackageName  PackageIdentifiers] = Names[PackageIdentifiers] processed in an implementation-defined manner;
Names[PackageIdentifiers  Identifier] = [Name[Identifier]];
Names[PackageIdentifiers0  PackageIdentifiers1 . Identifier] = Names[PackageIdentifiers1 [Name[Identifier]];
packageDatabasePackage{}  {};

Predefined Identifiers

proc createGlobalObject(): Package
return new PackagelocalBindings: {
stdExplicitConstBinding(internal::“internal”, Namespaceinternal),
stdConstBinding(public::“explicit”, Attributeglobal_explicit),
stdConstBinding(public::“enumerable”, Attributeglobal_enumerable),
stdConstBinding(public::“dynamic”, Attributeglobal_dynamic),
stdConstBinding(public::“static”, Attributeglobal_static),
stdConstBinding(public::“virtual”, Attributeglobal_virtual),
stdConstBinding(public::“final”, Attributeglobal_final),
stdConstBinding(public::“prototype”, Attributeglobal_prototype),
stdConstBinding(public::“unused”, Attributeglobal_unused),
stdFunction(public::“override”, global_override, 1),
stdConstBinding(public::“NaN”, NumberNaNf64),
stdConstBinding(public::“Infinity”, Number+f64),
stdConstBinding(public::“fNaN”, floatNaNf32),
stdConstBinding(public::“fInfinity”, float+f32),
stdConstBinding(public::“undefined”, Voidundefined),
stdFunction(public::“eval”, global_eval, 1),
stdFunction(public::“parseInt”, global_parseint, 2),
stdFunction(public::“parseLong”, global_parselong, 2),
stdFunction(public::“parseFloat”, global_parsefloat, 1),
stdFunction(public::“isNaN”, global_isnan, 1),
stdFunction(public::“isFinite”, global_isfinite, 1),
stdFunction(public::“decodeURI”, global_decodeuri, 1),
stdFunction(public::“decodeURIComponent”, global_decodeuricomponent, 1),
stdFunction(public::“encodeURI”, global_encodeuri, 1),
stdFunction(public::“encodeURIComponent”, global_encodeuricomponent, 1),
stdConstBinding(public::“Object”, ClassObject),
stdConstBinding(public::“Never”, ClassNever),
stdConstBinding(public::“Void”, ClassVoid),
stdConstBinding(public::“Null”, ClassNull),
stdConstBinding(public::“Boolean”, ClassBoolean),
stdConstBinding(public::“GeneralNumber”, ClassGeneralNumber),
stdConstBinding(public::“long”, Classlong),
stdConstBinding(public::“ulong”, Classulong),
stdConstBinding(public::“float”, Classfloat),
stdConstBinding(public::“Number”, ClassNumber),
stdConstBinding(public::“sbyte”, Classsbyte),
stdConstBinding(public::“byte”, Classbyte),
stdConstBinding(public::“short”, Classshort),
stdConstBinding(public::“ushort”, Classushort),
stdConstBinding(public::“int”, Classint),
stdConstBinding(public::“uint”, Classuint),
stdConstBinding(public::“char”, Classchar),
stdConstBinding(public::“String”, ClassString),
stdConstBinding(public::“Array”, ClassArray),
stdConstBinding(public::“Namespace”, ClassNamespace),
stdConstBinding(public::“Attribute”, ClassAttribute),
stdConstBinding(public::“Date”, ClassDate),
stdConstBinding(public::“RegExp”, ClassRegExp),
stdConstBinding(public::“Class”, ClassClass),
stdConstBinding(public::“Function”, ClassFunction),
stdConstBinding(public::“PrototypeFunction”, ClassPrototypeFunction),
stdConstBinding(public::“Package”, ClassPackage),
stdConstBinding(public::“Error”, ClassError),
stdConstBinding(public::“ArgumentError”, ClassArgumentError),
stdConstBinding(public::“AttributeError”, ClassAttributeError),
stdConstBinding(public::“ConstantError”, ClassConstantError),
stdConstBinding(public::“DefinitionError”, ClassDefinitionError),
stdConstBinding(public::“EvalError”, ClassEvalError),
stdConstBinding(public::“RangeError”, ClassRangeError),
stdConstBinding(public::“ReferenceError”, ClassReferenceError),
stdConstBinding(public::“SyntaxError”, ClassSyntaxError),
stdConstBinding(public::“TypeError”, ClassTypeError),
stdConstBinding(public::“UninitializedError”, ClassUninitializedError),
stdConstBinding(public::“URIError”, ClassURIError)},
archetypeObjectPrototype, name: “”, initializenone, sealedfalse, internalNamespaceinternal
end proc;

Built-in Namespaces

publicNamespace = new Namespacename: “public;
internalNamespace = new Namespacename: “internal;

Built-in Attributes

global_explicitCompoundAttributeCompoundAttributenamespaces: {}, explicittrue, enumerablefalse, dynamicfalse, categorynone, overrideModnone, prototypefalse, unusedfalse;
global_enumerableCompoundAttributeCompoundAttributenamespaces: {}, explicitfalse, enumerabletrue, dynamicfalse, categorynone, overrideModnone, prototypefalse, unusedfalse;
global_dynamicCompoundAttributeCompoundAttributenamespaces: {}, explicitfalse, enumerablefalse, dynamictrue, categorynone, overrideModnone, prototypefalse, unusedfalse;
global_staticCompoundAttributeCompoundAttributenamespaces: {}, explicitfalse, enumerablefalse, dynamicfalse, categorystatic, overrideModnone, prototypefalse, unusedfalse;
global_virtualCompoundAttributeCompoundAttributenamespaces: {}, explicitfalse, enumerablefalse, dynamicfalse, categoryvirtual, overrideModnone, prototypefalse, unusedfalse;
global_finalCompoundAttributeCompoundAttributenamespaces: {}, explicitfalse, enumerablefalse, dynamicfalse, categoryfinal, overrideModnone, prototypefalse, unusedfalse;
global_prototypeCompoundAttributeCompoundAttributenamespaces: {}, explicitfalse, enumerablefalse, dynamicfalse, categorynone, overrideModnone, prototypetrue, unusedfalse;
global_unusedCompoundAttributeCompoundAttributenamespaces: {}, explicitfalse, enumerablefalse, dynamicfalse, categorynone, overrideModnone, prototypefalse, unusedtrue;
proc global_override(thisObjectfSimpleInstanceargsObject[], phasePhase): Object
note  This function does not check phase and therefore can be used in a constant expression.
overrideModOverrideModifier;
if args = [] then overrideMod  true
elsif |args| = 1 then
argObject  args[0];
if arg  {truefalseundefinedthen throw a TypeError exception end if;
overrideMod  arg
else throw an ArgumentError exception — too many arguments supplied
end if;
return CompoundAttributenamespaces: {}, explicitfalse, enumerablefalse, dynamicfalse, categorynone, overrideModoverrideMod, prototypefalse, unusedfalse
end proc;

Built-in Functions

proc global_eval(thisObjectfSimpleInstanceargsObject[], phasePhase): Object
Evaluate ???? and ignore its result
end proc;
proc global_parseint(thisObjectfSimpleInstanceargsObject[], phasePhase): Float64
note  This function can be used in a constant expression if the arguments can be converted to primitives in constant expressions.
if |args {1, 2} then
throw an ArgumentError exception — at least one and at most two arguments must be supplied
end if;
sString  objectToString(args[0], phase);
radixInteger  objectToInteger(defaultArg(args, 1, +zerof64), phase);
i: (Integer – {0})  {+zero–zeroNaN stringPrefixToInteger(sradix);
end proc;
proc stringPrefixToInteger(sStringradixInteger): (Integer – {0})  {+zero–zeroNaN}
rInteger  radix;
if r  {0, 2 ... 36} then throw a RangeError exception — radix out of range end if;
iInteger  0;
while i < |sand the nonterminal WhiteSpaceOrLineTerminatorChar can expand into [s[i]] do
i  i + 1
end while;
sign: {–1, 1}  1;
if i < |sthen
if s[i] = ‘+’ then i  i + 1 elsif s[i] = ‘-’ then sign  –1; i  i + 1 end if
end if;
if r  {0, 16} and i + 2  |sand s[i ... i + 1]  {“0x”, “0X”} then
r  16;
i  i + 2
end if;
if r = 0 then r  10 end if;
nInteger  0;
startInteger  i;
digitIntegerOpt  0;
while i < |sand digit  none do
chChar16  s[i];
if ch  {‘0’ ... ‘9’} then digit  char16ToInteger(ch) – char16ToInteger(‘0’)
elsif ch  {‘A’ ... ‘Z’} then
digit  char16ToInteger(ch) – char16ToInteger(‘A’) + 10
elsif ch  {‘a’ ... ‘z’} then
digit  char16ToInteger(ch) – char16ToInteger(‘a’) + 10
else digit  none
end if;
if digit  none and digit  r then digit  none end if;
if digit  none then n  nr + digiti  i + 1 end if
end while;
if i = start then return NaN end if;
if n  0 then return nsign
elsif sign > 0 then return +zero
else return –zero
end if
end proc;
proc global_parselong(thisObjectfSimpleInstanceargsObject[], phasePhase): GeneralNumber
note  This function can be used in a constant expression if the arguments can be converted to primitives in constant expressions.
if |args {1, 2} then
throw an ArgumentError exception — at least one and at most two arguments must be supplied
end if;
sString  objectToString(args[0], phase);
radixInteger  objectToInteger(defaultArg(args, 1, +zerof64), phase);
i: (Integer – {0})  {+zero–zeroNaN stringPrefixToInteger(sradix);
case i of
{+zero–zerodo return 0long;
Integer do return integerToLong(i);
{NaNdo return NaNf64
end case
end proc;
proc global_parsefloat(thisObjectfSimpleInstanceargsObject[], phasePhase): Float64
note  This function can be used in a constant expression if its argument can be converted to a primitive in a constant expression.
if |args 1 then
throw an ArgumentError exception — exactly one argument must be supplied
end if;
sString  objectToString(args[0], phase);
Apply the lexer grammar with the start symbol StringDecimalLiteral to the string s. If the grammar can interpret neither s nor any prefix of s as an expansion of StringDecimalLiteral, then return NaNf64. Otherwise, let p be the longest prefix of s (possibly s itself) such that p is an expansion of StringDecimalLiteral.
qExtendedRational the value of the action Lex applied to p’s expansion of the nonterminal StringDecimalLiteral;
end proc;
proc global_isnan(thisObjectfSimpleInstanceargsObject[], phasePhase): Boolean
note  This function can be used in a constant expression if its argument can be converted to a primitive in a constant expression.
if |args 1 then
throw an ArgumentError exception — exactly one argument must be supplied
end if;
xGeneralNumber  objectToGeneralNumber(args[0], phase);
return x  {NaNf32NaNf64}
end proc;
proc global_isfinite(thisObjectfSimpleInstanceargsObject[], phasePhase): Boolean
note  This function can be used in a constant expression if its argument can be converted to a primitive in a constant expression.
if |args 1 then
throw an ArgumentError exception — exactly one argument must be supplied
end if;
xGeneralNumber  objectToGeneralNumber(args[0], phase);
return x  {NaNf32NaNf64+f32+f64f32f64}
end proc;
proc global_decodeuri(thisObjectfSimpleInstanceargsObject[], phasePhase): Object
Evaluate ???? and ignore its result
end proc;
proc global_decodeuricomponent(thisObject, fSimpleInstance, argsObject[], phasePhase): Object
Evaluate ???? and ignore its result
end proc;
proc global_encodeuri(thisObjectfSimpleInstanceargsObject[], phasePhase): Object
Evaluate ???? and ignore its result
end proc;
proc global_encodeuricomponent(thisObject, fSimpleInstance, argsObject[], phasePhase): Object
Evaluate ???? and ignore its result
end proc;

Built-in Classes

proc dummyCall(thisObjectcClassargsObject[], phasePhase): Object
Evaluate ???? and ignore its result
end proc;
proc dummyConstruct(cClassargsObject[], phasePhase): Object
Evaluate ???? and ignore its result
end proc;
prototypesSealedBoolean = false;

Object

ObjectClassnew ClasslocalBindings: {}, instanceProperties: {}, supernone, prototypeObjectPrototype, completetrue, name: “Object”, typeofString: “object”, dynamictrue, finalfalse, defaultValueundefined, defaultHinthintNumber, hasPropertyordinaryHasProperty, bracketReadordinaryBracketRead, bracketWriteordinaryBracketWrite, bracketDeleteordinaryBracketDelete, readordinaryRead, writeordinaryWrite, deleteordinaryDelete, enumerateordinaryEnumerate, callcallObject, constructconstructObject, initnone, isordinaryIs, coercecoerceObject;
proc callObject(thisObjectcClassargsObject[], phasePhase): Object
note  This function does not check phase and therefore can be used in a constant expression.
if |args| = 0 then return undefined
elsif |args| = 1 then return args[0]
else throw an ArgumentError exception — at most one argument can be supplied
end if
end proc;
proc constructObject(cClassargsObject[], phasePhase): Object
note  This function does not check phase and therefore can be used in a constant expression.
if |args| > 1 then
throw an ArgumentError exception — at most one argument can be supplied
end if;
oObject  defaultArg(args, 0, undefined);
if o  {nullundefinedthen
else return o
end if
end proc;
proc coerceObject(oObjectcClass): ObjectOpt
return o
end proc;
ObjectPrototypeSimpleInstancenew SimpleInstancelocalBindings: {
stdConstBinding(public::“constructor”, ClassObject),
stdFunction(public::“toString”, Object_toString, 0),
stdFunction(public::“toLocaleString”, Object_toLocaleString, 0),
stdFunction(public::“valueOf”, Object_valueOf, 0),
stdFunction(public::“hasOwnProperty”, Object_hasOwnProperty, 1),
stdFunction(public::“isPrototypeOf”, Object_isPrototypeOf, 1),
stdFunction(public::“propertyIsEnumerable”, Object_propertyIsEnumerable, 1),
stdFunction(public::“sealProperty”, Object_sealProperty, 1)},
archetypenone, sealedprototypesSealed, typeObject, slots: {}, callnone, constructnone, envnone;
proc Object_toString(thisObjectfSimpleInstanceargsObject[], phasePhase): String
note  This function does not check phase and therefore can be used in a constant expression.
note  This function ignores any arguments passed to it in args.
cClass  objectType(this);
return “[object ”  c.name  “]
end proc;
proc Object_toLocaleString(thisObjectfSimpleInstanceargsObject[], phasePhase): Object
if phase = compile then
throw a ConstantError exception — toLocaleString cannot be called from a constant expression
end if;
toStringMethodObject  dotRead(this, {public::“toString”}, phase);
return call(thistoStringMethodargsphase)
end proc;
proc Object_valueOf(thisObjectfSimpleInstanceargsObject[], phasePhase): Object
note  This function does not check phase and therefore can be used in a constant expression.
note  This function ignores any arguments passed to it in args.
return this
end proc;
proc Object_hasOwnProperty(thisObjectfSimpleInstanceargsObject[], phasePhase): Boolean
if phase = compile then
throw a ConstantError exception — hasOwnProperty cannot be called from a constant expression
end if;
if |args 1 then
throw an ArgumentError exception — exactly one argument must be supplied
end if;
return hasProperty(thisargs[0], truephase)
end proc;
proc Object_isPrototypeOf(thisObjectfSimpleInstanceargsObject[], phasePhase): Boolean
if phase = compile then
throw a ConstantError exception — isPrototypeOf cannot be called from a constant expression
end if;
if |args 1 then
throw an ArgumentError exception — exactly one argument must be supplied
end if;
oObject  args[0];
return this  archetypes(o)
end proc;
proc Object_propertyIsEnumerable(thisObject, fSimpleInstance, argsObject[], phasePhase): Boolean
if phase = compile then
throw a ConstantError exception — propertyIsEnumerable cannot be called from a constant expression
end if;
if |args 1 then
throw an ArgumentError exception — exactly one argument must be supplied
end if;
qnameQualifiedName  objectToQualifiedName(args[0], phase);
cClass  objectType(this);
mBaseInstancePropertyOpt  findBaseInstanceProperty(c, {qname}, read);
if mBase  none then
mInstanceProperty  getDerivedInstanceProperty(cmBaseread);
if m.enumerable then return true end if
end if;
mBase  findBaseInstanceProperty(c, {qname}, write);
if mBase  none then
mInstanceProperty  getDerivedInstanceProperty(cmBasewrite);
if m.enumerable then return true end if
end if;
if this  BindingObject then return false end if;
return some b  this.localBindings satisfies b.qname = qname and b.enumerable
end proc;
proc Object_sealProperty(thisObjectfSimpleInstanceargsObject[], phasePhase): Undefined
if phase = compile then
throw a ConstantError exception — sealProperty cannot be called from a constant expression
end if;
if |args| > 1 then
throw an ArgumentError exception — at most one argument can be supplied
end if;
argObject  defaultArg(args, 0, true);
if arg = false then Evaluate sealObject(this) and ignore its result
elsif arg = true then
Evaluate sealObject(this) and ignore its result;
Evaluate sealAllLocalProperties(this) and ignore its result
elsif arg  Char16  String then
if not hasProperty(thisargtruephasethen
throw a ReferenceError exception — property not found
end if;
qnameQualifiedName  objectToQualifiedName(argphase);
Evaluate sealLocalProperty(thisqname) and ignore its result
end if;
return undefined
end proc;

Never

NeverClassnew ClasslocalBindings: {}, instanceProperties: {}, superObject, prototypenone, completetrue, name: “Never”, typeofString: “”, dynamicfalse, finaltrue, defaultValuenone, hasPropertyordinaryHasProperty, bracketReadordinaryBracketRead, bracketWriteordinaryBracketWrite, bracketDeleteordinaryBracketDelete, readordinaryRead, writeordinaryWrite, deleteordinaryDelete, enumerateordinaryEnumerate, callsameAsConstruct, constructconstructNever, initnone, isordinaryIs, coercecoerceNever;
proc constructNever(cClassargsObject[], phasePhase): Object
if |args| > 1 then
throw an ArgumentError exception — at most one argument can be supplied
end if;
throw a TypeError exception — no coercions to Never are possible
end proc;
proc coerceNever(oObjectcClass): {none}
return none
end proc;

Void

VoidClassnew ClasslocalBindings: {}, instanceProperties: {}, superObject, prototypenone, completetrue, name: “Void”, typeofString: “undefined”, dynamicfalse, finaltrue, defaultValueundefined, hasPropertyordinaryHasProperty, bracketReadordinaryBracketRead, bracketWriteordinaryBracketWrite, bracketDeleteordinaryBracketDelete, readordinaryRead, writeordinaryWrite, deleteordinaryDelete, enumerateordinaryEnumerate, callcallVoid, constructconstructVoid, initnone, isordinaryIs, coercecoerceVoid;
proc callVoid(thisObjectcClassargsObject[], phasePhase): Undefined
note  This function does not check phase and therefore can be used in a constant expression.
if |args| > 1 then
throw an ArgumentError exception — at most one argument can be supplied
end if;
return undefined
end proc;
proc constructVoid(cClassargsObject[], phasePhase): Undefined
note  This function does not check phase and therefore can be used in a constant expression.
if |args 0 then throw an ArgumentError exception — no arguments can be supplied
end if;
return undefined
end proc;
proc coerceVoid(oObjectcClass): {undefinednone}
if o  Null  Undefined then return undefined else return none end if
end proc;

Null

NullClassnew ClasslocalBindings: {}, instanceProperties: {}, superObject, prototypenone, completetrue, name: “Null”, typeofString: “object”, dynamicfalse, finaltrue, defaultValuenull, hasPropertyordinaryHasProperty, bracketReadordinaryBracketRead, bracketWriteordinaryBracketWrite, bracketDeleteordinaryBracketDelete, readordinaryRead, writeordinaryWrite, deleteordinaryDelete, enumerateordinaryEnumerate, callcallNull, constructconstructNull, initnone, isordinaryIs, coercecoerceNull;
proc callNull(thisObjectcClassargsObject[], phasePhase): Null
note  This function does not check phase and therefore can be used in a constant expression.
if |args| > 1 then
throw an ArgumentError exception — at most one argument can be supplied
end if;
return null
end proc;
proc constructNull(cClassargsObject[], phasePhase): Null
note  This function does not check phase and therefore can be used in a constant expression.
if |args 0 then throw an ArgumentError exception — no arguments can be supplied
end if;
return null
end proc;
proc coerceNull(oObjectcClass): {nullnone}
if o = null then return o else return none end if
end proc;

Boolean

BooleanClassnew ClasslocalBindings: {}, instanceProperties: {}, superObject, prototypeBooleanPrototype, completetrue, name: “Boolean”, typeofString: “boolean”, dynamicfalse, finaltrue, defaultValuefalse, hasPropertyordinaryHasProperty, bracketReadordinaryBracketRead, bracketWriteordinaryBracketWrite, bracketDeleteordinaryBracketDelete, readordinaryRead, writeordinaryWrite, deleteordinaryDelete, enumerateordinaryEnumerate, callsameAsConstruct, constructconstructBoolean, initnone, isordinaryIs, coercecoerceBoolean;
proc constructBoolean(cClassargsObject[], phasePhase): Boolean
note  This function does not check phase and therefore can be used in a constant expression.
if |args| > 1 then
throw an ArgumentError exception — at most one argument can be supplied
end if;
return objectToBoolean(defaultArg(args, 0, false))
end proc;
proc coerceBoolean(oObjectcClass): BooleanOpt
if o  Boolean then return o else return none end if
end proc;
BooleanPrototypeSimpleInstancenew SimpleInstancelocalBindings: {
stdConstBinding(public::“constructor”, ClassBoolean),
stdFunction(public::“toString”, Boolean_toString, 0),
stdReserve(public::“valueOf”, ObjectPrototype)},
archetypeObjectPrototype, sealedprototypesSealed, typeObject, slots: {}, callnone, constructnone, envnone;
proc Boolean_toString(thisObjectfSimpleInstanceargsObject[], phasePhase): String
note  This function can be used in a constant expression.
note  This function ignores any arguments passed to it in args.
aBoolean  objectToBoolean(this);
return objectToString(aphase)
end proc;

GeneralNumber

GeneralNumberClassnew ClasslocalBindings: {}, instanceProperties: {}, superObject, prototypeGeneralNumberPrototype, completetrue, name: “GeneralNumber”, typeofString: “object”, dynamicfalse, finaltrue, defaultValueNaNf64, defaultHinthintNumber, hasPropertyordinaryHasProperty, bracketReadordinaryBracketRead, bracketWriteordinaryBracketWrite, bracketDeleteordinaryBracketDelete, readordinaryRead, writeordinaryWrite, deleteordinaryDelete, enumerateordinaryEnumerate, callsameAsConstruct, constructconstructGeneralNumber, initnone, isordinaryIs, coercecoerceGeneralNumber;
proc constructGeneralNumber(cClassargsObject[], phasePhase): GeneralNumber
note  This function can be used in a constant expression if the argument can be converted to a primitive in a constant expression.
if |args| = 0 then return +zerof64
elsif |args| = 1 then return objectToGeneralNumber(args[0], phase)
else throw an ArgumentError exception — at most one argument can be supplied
end if
end proc;
proc coerceGeneralNumber(oObjectcClass): GeneralNumber  {none}
if o  GeneralNumber then return o else return none end if
end proc;
GeneralNumberPrototypeSimpleInstancenew SimpleInstancelocalBindings: {
stdConstBinding(public::“constructor”, ClassGeneralNumber),
stdFunction(public::“toString”, GeneralNumber_toString, 1),
stdReserve(public::“valueOf”, ObjectPrototype),
stdFunction(public::“toFixed”, GeneralNumber_toFixed, 1),
stdFunction(public::“toExponential”, GeneralNumber_toExponential, 1),
stdFunction(public::“toPrecision”, GeneralNumber_toPrecision, 1)},
archetypeObjectPrototype, sealedprototypesSealed, typeObject, slots: {}, callnone, constructnone, envnone;
proc GeneralNumber_toString(thisObjectfSimpleInstanceargsObject[], phasePhase): String
note  This function can be used in a constant expression if this and the argument can be converted to primitives in constant expressions.
note  This function is generic and can be applied even if this is not a general number.
xGeneralNumber  objectToGeneralNumber(thisphase);
radixInteger  objectToInteger(defaultArg(args, 0, 10f64), phase);
if radix < 2 or radix > 36 then throw a RangeError exception — bad radix end if;
if radix = 10 then return generalNumberToString(x)
else
return x converted to a string containing a base-radix number in an implementation-defined manner
end if
end proc;
precisionLimitInteger = an implementation-defined integer not less than 20;
proc GeneralNumber_toFixed(thisObjectfSimpleInstanceargsObject[], phasePhase): String
note  This function can be used in a constant expression if this and the argument can be converted to primitives in constant expressions.
note  This function is generic and can be applied even if this is not a general number.
if |args| > 1 then
throw an ArgumentError exception — at most one argument can be supplied
end if;
xGeneralNumber  objectToGeneralNumber(thisphase);
fractionDigitsInteger  objectToInteger(defaultArg(args, 0, +zerof64), phase);
if fractionDigits < 0 or fractionDigits > precisionLimit then
throw a RangeError exception
end if;
if x  FiniteGeneralNumber then return generalNumberToString(xend if;
rRational  toRational(x);
if |r 1021 then return generalNumberToString(xend if;
signString  “”;
if r < 0 then sign  “-”; r  –r end if;
nInteger  r10fractionDigits + 1/2;
digitsString  integerToString(n);
if fractionDigits = 0 then return sign  digits
else
if |digits fractionDigits then
digits  repeat(‘0’, fractionDigits + 1 – |digits|)  digits
end if;
kInteger  |digits| – fractionDigits;
return sign  digits[0 ... k – 1]  “.”  digits[k ...]
end if
end proc;
proc GeneralNumber_toExponential(thisObject, fSimpleInstance, argsObject[], phasePhase): String
note  This function can be used in a constant expression if this and the argument can be converted to primitives in constant expressions.
note  This function is generic and can be applied even if this is not a general number.
if |args| > 1 then
throw an ArgumentError exception — at most one argument can be supplied
end if;
xGeneralNumber  objectToGeneralNumber(thisphase);
fractionDigitsExtendedInteger  objectToExtendedInteger(defaultArg(args, 0, NaNf64), phase);
if fractionDigits  {+or (fractionDigits  NaN and (fractionDigits < 0 or fractionDigits > precisionLimit)) then
throw a RangeError exception
end if;
if x  FiniteGeneralNumber then return generalNumberToString(xend if;
rRational  toRational(x);
signString  “”;
if r < 0 then sign  “-”; r  –r end if;
digitsString;
eInteger;
if fractionDigits  NaN then
if r = 0 then digits  repeat(‘0’, fractionDigits + 1); e  0
else
e  log10(r);
nInteger  r10fractionDigitse + 1/2;
note  At this point 10fractionDigits  n  10fractionDigits+1
if n = 10fractionDigits+1 then n  n/10; e  e + 1 end if;
digits  integerToString(n)
end if;
note  At this point the string digits has exactly fractionDigits + 1 digits
elsif r = 0 then digits  “0”; e  0
elsif x  Long  ULong then
digits  integerToString(r);
e  |digits| – 1;
while digits[|digits| – 1] = ‘0’ do digits  digits[0 ... |digits| – 2] end while
else
kInteger;
sInteger;
case x of
Let e, k, and s be integers such that k 1, 10k–1 s 10k, (s10e+1–k)f32 = x, and k is as small as possible.
Let e, k, and s be integers such that k 1, 10k–1 s 10k, (s10e+1–k)f64 = x, and k is as small as possible.
end case;
note  k is the number of digits in the decimal representation of s, s is not divisible by 10, and the least significant digit of s is not necessarily uniquely determined by the above criteria.
When there are multiple possibilities for s according to the rules above, implementations are encouraged but not required to select the one according to the following rules: Select the value of s for which s10e+1–k is closest in value to r; if there are two such possible values of s, choose the one that is even.
digits  integerToString(s)
end if;
return sign  exponentialNotationString(digitse)
end proc;
proc GeneralNumber_toPrecision(thisObject, fSimpleInstance, argsObject[], phasePhase): String
note  This function can be used in a constant expression if this and the argument can be converted to primitives in constant expressions.
note  This function is generic and can be applied even if this is not a general number.
if |args| > 1 then
throw an ArgumentError exception — at most one argument can be supplied
end if;
xGeneralNumber  objectToGeneralNumber(thisphase);
precisionExtendedInteger  objectToExtendedInteger(defaultArg(args, 0, NaNf64), phase);
if precision = NaN then return generalNumberToString(xend if;
if precision  {+or precision < 1 or precision > precisionLimit + 1 then
throw a RangeError exception
end if;
if x  FiniteGeneralNumber then return generalNumberToString(xend if;
rRational  toRational(x);
signString  “”;
if r < 0 then sign  “-”; r  –r end if;
digitsString;
eInteger;
if r = 0 then digits  repeat(‘0’, precision); e  0
else
e  log10(r);
nInteger  r10precision–1–e + 1/2;
note  At this point 10precision–1  n  10precision
if n = 10precision then n  n/10; e  e + 1 end if;
digits  integerToString(n)
end if;
note  At this point the string digits has exactly precision digits
if e < –6 or e  precision then return sign  exponentialNotationString(digitse)
elsif e = precision – 1 then return sign  digits
elsif e  0 then return sign  digits[0 ... e “.”  digits[e + 1 ...]
else return sign  “0.”  repeat(‘0’, –(e + 1))  digits
end if
end proc;

long

longClassnew ClasslocalBindings: {
stdConstBinding(public::“MAX_VALUE”, ulong, (263 – 1)long),
stdConstBinding(public::“MIN_VALUE”, ulong, (–263)long)},
instanceProperties: {}, superGeneralNumber, prototypelongPrototype, completetrue, name: “long”, typeofString: “long”, dynamicfalse, finaltrue, defaultValue: 0long, hasPropertyordinaryHasProperty, bracketReadordinaryBracketRead, bracketWriteordinaryBracketWrite, bracketDeleteordinaryBracketDelete, readordinaryRead, writeordinaryWrite, deleteordinaryDelete, enumerateordinaryEnumerate, callsameAsConstruct, constructconstructLong, initnone, isordinaryIs, coercecoerceLong;
proc constructLong(cClassargsObject[], phasePhase): Long
note  This function can be used in a constant expression if the argument can be converted to a primitive in a constant expression.
if |args| > 1 then
throw an ArgumentError exception — at most one argument can be supplied
end if;
argObject  defaultArg(args, 0, +zerof64);
iInteger  objectToInteger(argphase);
if –263  i  263 – 1 then return ilong
else throw a RangeError exception — i is out of the Long range
end if
end proc;
proc coerceLong(oObjectcClass): Long  {none}
if o  GeneralNumber then return none end if;
iIntegerOpt  checkInteger(o);
if i  none and –263  i  263 – 1 then return ilong
else throw a RangeError exception — i is out of the Long range
end if
end proc;
longPrototypeSimpleInstancenew SimpleInstancelocalBindings: {
stdConstBinding(public::“constructor”, Classlong),
stdReserve(public::“toString”, GeneralNumberPrototype),
stdReserve(public::“valueOf”, GeneralNumberPrototype)},
archetypeGeneralNumberPrototype, sealedprototypesSealed, typeObject, slots: {}, callnone, constructnone, envnone;

ulong

ulongClassnew ClasslocalBindings: {
stdConstBinding(public::“MAX_VALUE”, ulong, (264 – 1)ulong),
stdConstBinding(public::“MIN_VALUE”, ulong, 0ulong)},
instanceProperties: {}, superGeneralNumber, prototypeulongPrototype, completetrue, name: “ulong”, typeofString: “ulong”, dynamicfalse, finaltrue, defaultValue: 0ulong, hasPropertyordinaryHasProperty, bracketReadordinaryBracketRead, bracketWriteordinaryBracketWrite, bracketDeleteordinaryBracketDelete, readordinaryRead, writeordinaryWrite, deleteordinaryDelete, enumerateordinaryEnumerate, callsameAsConstruct, constructconstructULong, initnone, isordinaryIs, coercecoerceULong;
proc constructULong(cClassargsObject[], phasePhase): ULong
note  This function can be used in a constant expression if the argument can be converted to a primitive in a constant expression.
if |args| > 1 then
throw an ArgumentError exception — at most one argument can be supplied
end if;
argObject  defaultArg(args, 0, +zerof64);
iInteger  objectToInteger(argphase);
if 0  i  264 – 1 then return iulong
else throw a RangeError exception — i is out of the ULong range
end if
end proc;
proc coerceULong(oObjectcClass): ULong  {none}
if o  GeneralNumber then return none end if;
iIntegerOpt  checkInteger(o);
if i  none and 0  i  264 – 1 then return iulong
else throw a RangeError exception — i is out of the ULong range
end if
end proc;
ulongPrototypeSimpleInstancenew SimpleInstancelocalBindings: {
stdConstBinding(public::“constructor”, Classulong),
stdReserve(public::“toString”, GeneralNumberPrototype),
stdReserve(public::“valueOf”, GeneralNumberPrototype)},
archetypeGeneralNumberPrototype, sealedprototypesSealed, typeObject, slots: {}, callnone, constructnone, envnone;

float

floatClassnew ClasslocalBindings: {
stdConstBinding(public::“MAX_VALUE”, float, (3.40282351038)f32),
stdConstBinding(public::“MIN_VALUE”, float, (10–45)f32),
stdConstBinding(public::“NaN”, floatNaNf32),
stdConstBinding(public::“NEGATIVE_INFINITY”, floatf32),
stdConstBinding(public::“POSITIVE_INFINITY”, float+f32)},
instanceProperties: {}, superGeneralNumber, prototypefloatPrototype, completetrue, name: “float”, typeofString: “float”, dynamicfalse, finaltrue, defaultValueNaNf32, hasPropertyordinaryHasProperty, bracketReadordinaryBracketRead, bracketWriteordinaryBracketWrite, bracketDeleteordinaryBracketDelete, readordinaryRead, writeordinaryWrite, deleteordinaryDelete, enumerateordinaryEnumerate, callsameAsConstruct, constructconstructFloat, initnone, isordinaryIs, coercecoerceFloat;
proc constructFloat(cClassargsObject[], phasePhase): Float32
note  This function can be used in a constant expression if the argument can be converted to a primitive in a constant expression.
if |args| = 0 then return +zerof32
elsif |args| = 1 then return objectToFloat32(args[0], phase)
else throw an ArgumentError exception — at most one argument can be supplied
end if
end proc;
proc coerceFloat(oObjectcClass): Float32  {none}
if o  GeneralNumber then return toFloat32(oelse return none end if
end proc;
floatPrototypeSimpleInstancenew SimpleInstancelocalBindings: {
stdConstBinding(public::“constructor”, Classfloat),
stdReserve(public::“toString”, GeneralNumberPrototype),
stdReserve(public::“valueOf”, GeneralNumberPrototype)},
archetypeGeneralNumberPrototype, sealedprototypesSealed, typeObject, slots: {}, callnone, constructnone, envnone;

Number

NumberClassnew ClasslocalBindings: {
stdConstBinding(public::“MAX_VALUE”, Number, (1.797693134862315710308)f64),
stdConstBinding(public::“MIN_VALUE”, Number, (510–324)f64),
stdConstBinding(public::“NaN”, NumberNaNf64),
stdConstBinding(public::“NEGATIVE_INFINITY”, Numberf64),
stdConstBinding(public::“POSITIVE_INFINITY”, Number+f64)},
instanceProperties: {}, superGeneralNumber, prototypeNumberPrototype, completetrue, name: “Number”, typeofString: “number”, dynamicfalse, finaltrue, defaultValueNaNf64, hasPropertyordinaryHasProperty, bracketReadordinaryBracketRead, bracketWriteordinaryBracketWrite, bracketDeleteordinaryBracketDelete, readordinaryRead, writeordinaryWrite, deleteordinaryDelete, enumerateordinaryEnumerate, callsameAsConstruct, constructconstructNumber, initnone, isordinaryIs, coercecoerceNumber;
proc constructNumber(cClassargsObject[], phasePhase): Float64
note  This function can be used in a constant expression if the argument can be converted to a primitive in a constant expression.
if |args| = 0 then return +zerof64
elsif |args| = 1 then return objectToFloat64(args[0], phase)
else throw an ArgumentError exception — at most one argument can be supplied
end if
end proc;
proc coerceNumber(oObjectcClass): Float64  {none}
if o  GeneralNumber then return toFloat64(oelse return none end if
end proc;
NumberPrototypeSimpleInstancenew SimpleInstancelocalBindings: {
stdConstBinding(public::“constructor”, ClassNumber),
stdReserve(public::“toString”, GeneralNumberPrototype),
stdReserve(public::“valueOf”, GeneralNumberPrototype)},
archetypeGeneralNumberPrototype, sealedprototypesSealed, typeObject, slots: {}, callnone, constructnone, envnone;
proc makeBuiltInIntegerClass(nameStringlowIntegerhighInteger): Class
proc construct(cClassargsObject[], phasePhase): Float64
note  This function can be used in a constant expression if the argument can be converted to a primitive in a constant expression.
if |args| > 1 then
throw an ArgumentError exception — at most one argument can be supplied
end if;
argObject  defaultArg(args, 0, +zerof64);
xFloat64  objectToFloat64(argphase);
iIntegerOpt  checkInteger(x);
if i  none and low  i  high then
note  –zerof64 is coerced to +zerof64.
return if64
end if;
throw a RangeError exception
end proc;
proc is(oObjectcClass): Boolean
if o  Float64 then return false end if;
iIntegerOpt  checkInteger(o);
return i  none and low  i  high
end proc;
proc coerce(oObjectcClass): Float64  {none}
if o  GeneralNumber then return none end if;
iIntegerOpt  checkInteger(o);
if i  none and low  i  high then
note  –zerof32, +zerof32, and –zerof64 are all coerced to +zerof64.
return if64
end if;
throw a RangeError exception
end proc;
return new ClasslocalBindings: {
stdConstBinding(public::“MAX_VALUE”, Numberhighf64),
stdConstBinding(public::“MIN_VALUE”, Numberlowf64)},
instanceProperties: {}, superNumber, prototypeNumber.prototype, completetrue, namename, typeofString: “number”, dynamicfalse, finaltrue, defaultValue+zerof64, hasPropertyNumber.hasProperty, bracketReadNumber.bracketRead, bracketWriteNumber.bracketWrite, bracketDeleteNumber.bracketDelete, readNumber.read, writeNumber.write, deleteNumber.delete, enumerateNumber.enumerate, callsameAsConstruct, constructconstruct, initnone, isis, coercecoerce
end proc;
sbyteClass = makeBuiltInIntegerClass(“sbyte”, –128, 127);
byteClass = makeBuiltInIntegerClass(“byte”, 0, 255);
shortClass = makeBuiltInIntegerClass(“short”, –32768, 32767);
ushortClass = makeBuiltInIntegerClass(“ushort”, 0, 65535);
intClass = makeBuiltInIntegerClass(“int”, –2147483648, 2147483647);
uintClass = makeBuiltInIntegerClass(“uint”, 0, 4294967295);

char

charClassnew ClasslocalBindings: {stdFunction(public::“fromCharCode”, char_fromCharCode, 1)}, instanceProperties: {}, superObject, prototypecharPrototype, completetrue, name: “char”, typeofString: “char”, dynamicfalse, finaltrue, defaultValue: ‘«NUL»’, hasPropertyordinaryHasProperty, bracketReadordinaryBracketRead, bracketWriteordinaryBracketWrite, bracketDeleteordinaryBracketDelete, readordinaryRead, writeordinaryWrite, deleteordinaryDelete, enumerateordinaryEnumerate, callsameAsConstruct, constructconstructChar, initnone, isordinaryIs, coercecoerceChar;
proc callChar(thisObjectcClassargsObject[], phasePhase): Char16
note  This function can be used in a constant expression if the argument can be converted to a primitive in a constant expression.
if |args 1 then
throw an ArgumentError exception — exactly one argument must be supplied
end if;
sString  objectToString(args[0], phase);
if |s 1 then throw a RangeError exception — only one character may be given end if;
return s[0]
end proc;
proc constructChar(cClassargsObject[], phasePhase): Char16
note  This function can be used in a constant expression if the argument can be converted to a primitive in a constant expression.
if |args| > 1 then
throw an ArgumentError exception — at most one argument can be supplied
end if;
argObject  defaultArg(args, 0, undefined);
if arg = undefined then return ‘«NUL»
elsif arg  Char16 then return arg
else
sString  objectToString(args[0], phase);
if |s 1 then throw a RangeError exception — only one character may be given
end if;
return s[0]
end if
end proc;
proc coerceChar(oObjectcClass): Char16  {none}
if o  Char16 then return o else return none end if
end proc;
proc char_fromCharCode(thisObjectfSimpleInstanceargsObject[], phasePhase): Object
note  This function can be used in a constant expression if the argument can be converted to a primitive in a constant expression.
if |args 1 then
throw an ArgumentError exception — exactly one argument must be supplied
end if;
iInteger  objectToInteger(args[0], phase);
if 0  i  0xFFFF then return integerToChar16(i)
else throw a RangeError exception — character code out of range
end if
end proc;
charPrototypeSimpleInstancenew SimpleInstancelocalBindings: {
stdConstBinding(public::“constructor”, Classchar),
stdReserve(public::“toString”, StringPrototype),
stdReserve(public::“valueOf”, StringPrototype)},
archetypeStringPrototype, sealedprototypesSealed, typeObject, slots: {}, callnone, constructnone, envnone;

String

StringClassnew ClasslocalBindings: {stdFunction(public::“fromCharCode”, String_fromCharCode, 1)}, instanceProperties: {
new InstanceGettermultiname: {public::“length”}, finaltrue, enumerablefalse, callString_length},
superObject, prototypeStringPrototype, completetrue, name: “String”, typeofString: “string”, dynamicfalse, finaltrue, defaultValuenull, hasPropertystringHasProperty, bracketReadordinaryBracketRead, bracketWriteordinaryBracketWrite, bracketDeleteordinaryBracketDelete, readreadString, writeordinaryWrite, deleteordinaryDelete, enumerateordinaryEnumerate, callsameAsConstruct, constructconstructString, initnone, isordinaryIs, coercecoerceString;
proc stringHasProperty(oObjectcClasspropertyObjectflatBooleanphasePhase): Boolean
note  o  String because stringHasProperty is only called on instances of class String.
qnameQualifiedName  objectToQualifiedName(propertyphase);
iIntegerOpt  multinameToUnsignedInteger({qname});
if i  none then return i < |o|
else
return findBaseInstanceProperty(c, {qname}, read none or findBaseInstanceProperty(c, {qname}, write none or findArchetypeProperty(o, {qname}, readflat none or findArchetypeProperty(o, {qname}, writeflat none
end if
end proc;
proc readString(oObject, limitClass, multinameMultiname, envEnvironmentOpt, undefinedIfMissingBoolean, phasePhase): ObjectOpt
note  o  String because readString is only called on instances of class String.
if limit = String then
iIntegerOpt  multinameToUnsignedInteger(multiname);
if i  none then
if i < |othen return o[i]
elsif undefinedIfMissing then return undefined
else return none
end if
end if
end if;
return ordinaryRead(olimitmultinameenvundefinedIfMissingphase)
end proc;
proc constructString(cClassargsObject[], phasePhase): String
note  This function can be used in a constant expression if the argument can be converted to a primitive in a constant expression.
if |args| = 0 then return “”
elsif |args| = 1 then return objectToString(args[0], phase)
else throw an ArgumentError exception — at most one argument can be supplied
end if
end proc;
proc coerceString(oObjectcClass): String  Null  {none}
if o  Null  String then return o
elsif o  Char16 then return [o]
else return none
end if
end proc;
proc String_length(thisObjectphasePhase): Object
note  this  String because this getter cannot be extracted from the String class.
lengthInteger  |this|;
return lengthf64
end proc;
proc String_fromCharCode(thisObjectfSimpleInstanceargsObject[], phasePhase): Object
note  This function can be used in a constant expression if the arguments can be converted to primitives in constant expressions.
sString  “”;
for each arg  args do
iInteger  objectToInteger(argphase);
if 0  i  0x10FFFF then s  s  integerToUTF16(i)
else throw a RangeError exception — character code out of range
end if
end for each;
return s
end proc;
StringPrototypeSimpleInstancenew SimpleInstancelocalBindings: {
stdConstBinding(public::“constructor”, ClassString),
stdFunction(public::“toString”, String_toString, 0),
stdReserve(public::“valueOf”, ObjectPrototype),
stdFunction(public::“charAt”, String_charAt, 1),
stdFunction(public::“charCodeAt”, String_charCodeAt, 1),
stdFunction(public::“concat”, String_concat, 1),
stdFunction(public::“indexOf”, String_indexOf, 1),
stdFunction(public::“lastIndexOf”, String_lastIndexOf, 1),
stdFunction(public::“localeCompare”, String_localeCompare, 1),
stdFunction(public::“match”, String_match, 1),
stdFunction(public::“replace”, String_replace, 1),
stdFunction(public::“search”, String_search, 1),
stdFunction(public::“slice”, String_slice, 2),
stdFunction(public::“split”, String_split, 2),
stdFunction(public::“substring”, String_substring, 2),
stdFunction(public::“toLowerCase”, String_toLowerCase, 0),
stdFunction(public::“toLocaleLowerCase”, String_toLocaleLowerCase, 0),
stdFunction(public::“toUpperCase”, String_toUpperCase, 0),
stdFunction(public::“toLocaleUpperCase”, String_toLocaleUpperCase, 0)},
archetypeObjectPrototype, sealedprototypesSealed, typeObject, slots: {}, callnone, constructnone, envnone;
proc String_toString(thisObjectfSimpleInstanceargsObject[], phasePhase): String
note  This function can be used in a constant expression if this can be converted to a primitive in a constant expression.
note  This function is generic and can be applied even if this is not a string.
note  This function ignores any arguments passed to it in args.
return objectToString(thisphase)
end proc;
proc String_charAt(thisObjectfSimpleInstanceargsObject[], phasePhase): String
note  This function can be used in a constant expression if this and the argument can be converted to primitives in constant expressions.
note  This function is generic and can be applied even if this is not a string.
if |args| > 1 then
throw an ArgumentError exception — at most one argument can be supplied
end if;
sString  objectToString(thisphase);
positionExtendedInteger  objectToExtendedInteger(defaultArg(args, 0, +zerof64), phase);
if position = NaN then throw a RangeError exception
elsif position  {+and 0  position < |sthen return [s[position]]
else return “”
end if
end proc;
proc String_charCodeAt(thisObjectfSimpleInstanceargsObject[], phasePhase): Float64
note  This function can be used in a constant expression if this and the argument can be converted to primitives in constant expressions.
note  This function is generic and can be applied even if this is not a string.
if |args| > 1 then
throw an ArgumentError exception — at most one argument can be supplied
end if;
sString  objectToString(thisphase);
positionExtendedInteger  objectToExtendedInteger(defaultArg(args, 0, +zerof64), phase);
if position = NaN then throw a RangeError exception
elsif position  {+and 0  position < |sthen
return (char16ToInteger(s[position]))f64
else return NaNf64
end if
end proc;
proc String_concat(thisObjectfSimpleInstanceargsObject[], phasePhase): String
note  This function can be used in a constant expression if this and the argument can be converted to primitives in constant expressions.
note  This function is generic and can be applied even if this is not a string.
sString  objectToString(thisphase);
for each arg  args do s  s  objectToString(argphaseend for each;
return s
end proc;
proc String_indexOf(thisObjectfSimpleInstanceargsObject[], phasePhase): Float64
note  This function can be used in a constant expression if this and the arguments can be converted to primitives in constant expressions.
note  This function is generic and can be applied even if this is not a string.
if |args {1, 2} then
throw an ArgumentError exception — at least one and at most two arguments must be supplied
end if;
sString  objectToString(thisphase);
patternString  objectToString(args[0], phase);
argObject  defaultArg(args, 1, +zerof64);
positionInteger  pinExtendedInteger(objectToExtendedInteger(argphase), |s|, false);
while position + |pattern |sdo
if s[position ... position + |pattern| – 1] = pattern then return positionf64
end if;
position  position + 1
end while;
return (–1)f64
end proc;
proc String_lastIndexOf(thisObjectfSimpleInstanceargsObject[], phasePhase): Float64
note  This function can be used in a constant expression if this and the arguments can be converted to primitives in constant expressions.
note  This function is generic and can be applied even if this is not a string.
if |args {1, 2} then
throw an ArgumentError exception — at least one and at most two arguments must be supplied
end if;
sString  objectToString(thisphase);
patternString  objectToString(args[0], phase);
argObject  defaultArg(args, 1, +f64);
positionInteger  pinExtendedInteger(objectToExtendedInteger(argphase), |s|, false);
if position + |pattern| > |sthen position  |s| – |patternend if;
while position  0 do
if s[position ... position + |pattern| – 1] = pattern then return positionf64
end if;
position  position – 1
end while;
return (–1)f64
end proc;
proc String_localeCompare(thisObjectfSimpleInstanceargsObject[], phasePhase): Float64
note  This function is generic and can be applied even if this is not a string.
if phase = compile then
throw a ConstantError exception — localeCompare cannot be called from a constant expression
end if;
if |args| < 1 then
throw an ArgumentError exception — at least one argument must be supplied
end if;
s1String  objectToString(thisphase);
s2String  objectToString(args[0], phase);
Let resultObject be a value of type Number that is the result of a locale-sensitive string comparison of s1 and s2. The two strings are compared in an implementation-defined fashion. The result is intended to order strings in the sort order specified by the system default locale, and will be negative, zero, or positive, depending on whether s1 comes before s2 in the sort order, they are equal, or s1 comes after s2 in the sort order, respectively. The result shall not be NaNf64. The comparison shall be a consistent comparison function on the set of all strings.
return result
end proc;
proc String_match(thisObjectfSimpleInstanceargsObject[], phasePhase): Object
note  This function is generic and can be applied even if this is not a string.
if phase = compile then
throw a ConstantError exception — match cannot be called from a constant expression
end if;
if |args 1 then
throw an ArgumentError exception — exactly one argument must be supplied
end if;
sString  objectToString(thisphase);
Evaluate ???? and ignore its result
end proc;
proc String_replace(thisObjectfSimpleInstanceargsObject[], phasePhase): Object
note  This function is generic and can be applied even if this is not a string.
if phase = compile then
throw a ConstantError exception — replace cannot be called from a constant expression
end if;
if |args 2 then
throw an ArgumentError exception — exactly two arguments must be supplied
end if;
sString  objectToString(thisphase);
Evaluate ???? and ignore its result
end proc;
proc String_search(thisObjectfSimpleInstanceargsObject[], phasePhase): Object
note  This function is generic and can be applied even if this is not a string.
if phase = compile then
throw a ConstantError exception — search cannot be called from a constant expression
end if;
if |args 1 then
throw an ArgumentError exception — exactly one argument must be supplied
end if;
sString  objectToString(thisphase);
Evaluate ???? and ignore its result
end proc;
proc String_slice(thisObjectfSimpleInstanceargsObject[], phasePhase): String
note  This function can be used in a constant expression if this and the arguments can be converted to primitives in constant expressions.
note  This function is generic and can be applied even if this is not a string.
if |args| > 2 then
throw an ArgumentError exception — at most two arguments can be supplied
end if;
sString  objectToString(thisphase);
startArgObject  defaultArg(args, 0, +zerof64);
endArgObject  defaultArg(args, 1, +f64);
startInteger  pinExtendedInteger(objectToExtendedInteger(startArgphase), |s|, true);
endInteger  pinExtendedInteger(objectToExtendedInteger(endArgphase), |s|, true);
if start < end then return s[start ... end – 1] else return “” end if
end proc;
proc String_split(thisObjectfSimpleInstanceargsObject[], phasePhase): Object
note  This function is generic and can be applied even if this is not a string.
if phase = compile then
throw a ConstantError exception — split cannot be called from a constant expression
end if;
if |args| > 2 then
throw an ArgumentError exception — at most two arguments can be supplied
end if;
sString  objectToString(thisphase);
Evaluate ???? and ignore its result
end proc;
proc String_substring(thisObjectfSimpleInstanceargsObject[], phasePhase): String
note  This function can be used in a constant expression if this and the arguments can be converted to primitives in constant expressions.
note  This function is generic and can be applied even if this is not a string.
if |args| > 2 then
throw an ArgumentError exception — at most two arguments can be supplied
end if;
sString  objectToString(thisphase);
startArgObject  defaultArg(args, 0, +zerof64);
endArgObject  defaultArg(args, 1, +f64);
startInteger  pinExtendedInteger(objectToExtendedInteger(startArgphase), |s|, false);
endInteger  pinExtendedInteger(objectToExtendedInteger(endArgphase), |s|, false);
if start  end then return s[start ... end – 1]
else return s[end ... start – 1]
end if
end proc;
proc String_toLowerCase(thisObjectfSimpleInstanceargsObject[], phasePhase): String
note  This function can be used in a constant expression if this can be converted to a primitive in a constant expression.
note  This function is generic and can be applied even if this is not a string.
sString  objectToString(thisphase);
s32Char21[]  stringToUTF32(s);
rString  “”;
for each ch  s32 do r  r  charToLowerFull(chend for each;
return r
end proc;
proc String_toLocaleLowerCase(thisObject, fSimpleInstance, argsObject[], phasePhase): String
note  This function is generic and can be applied even if this is not a string.
if phase = compile then
throw a ConstantError exception — toLocaleLowerCase cannot be called from a constant expression
end if;
sString  objectToString(thisphase);
s32Char21[]  stringToUTF32(s);
rString  “”;
for each ch  s32 do r  r  charToLowerLocalized(chend for each;
return r
end proc;
proc String_toUpperCase(thisObjectfSimpleInstanceargsObject[], phasePhase): String
note  This function can be used in a constant expression if this can be converted to a primitive in a constant expression.
note  This function is generic and can be applied even if this is not a string.
sString  objectToString(thisphase);
s32Char21[]  stringToUTF32(s);
rString  “”;
for each ch  s32 do r  r  charToUpperFull(chend for each;
return r
end proc;
proc String_toLocaleUpperCase(thisObject, fSimpleInstance, argsObject[], phasePhase): String
note  This function is generic and can be applied even if this is not a string.
if phase = compile then
throw a ConstantError exception — toLocaleUpperCase cannot be called from a constant expression
end if;
sString  objectToString(thisphase);
s32Char21[]  stringToUTF32(s);
rString  “”;
for each ch  s32 do r  r  charToUpperLocalized(chend for each;
return r
end proc;

Array

ArrayClassnew ClasslocalBindings: {}, instanceProperties: {
new InstanceVariablemultiname: {arrayPrivate::“length”}, finaltrue, enumerablefalse, typeNumber, defaultValue+zerof64, immutablefalse,
new InstanceGettermultiname: {public::“length”}, finaltrue, enumerablefalse, callArray_getLength,
new InstanceSettermultiname: {public::“length”}, finaltrue, enumerablefalse, callArray_setLength},
superObject, prototypeArrayPrototype, completetrue, name: “Array”, typeofString: “object”, privateNamespacearrayPrivate, dynamictrue, finaltrue, defaultValuenull, defaultHinthintNumber, hasPropertyordinaryHasProperty, bracketReadordinaryBracketRead, bracketWriteordinaryBracketWrite, bracketDeleteordinaryBracketDelete, readordinaryRead, writewriteArray, deleteordinaryDelete, enumerateordinaryEnumerate, callsameAsConstruct, constructordinaryConstruct, initinitArray, isordinaryIs, coerceordinaryCoerce;
arrayLimitInteger = an implementation-defined integer value between 232 – 1 and 253 inclusive;
arrayPrivateNamespace = new Namespacename: “private;
proc writeArray(oObject, limitClass, multinameMultiname, envEnvironmentOpt, newValueObject, createIfMissingBoolean, phase: {run}): {noneok}
result: {noneok ordinaryWrite(olimitmultinameenvnewValuecreateIfMissingphase);
if result = ok then
iIntegerOpt  multinameToUnsignedInteger(multiname);
if i  none then
if i  arrayLimit then
throw a RangeError exception — array index out of range
end if;
lengthInteger  readArrayPrivateLength(ophase);
if i  length then
length  i + 1;
Evaluate writeArrayPrivateLength(olengthphase) and ignore its result
end if
end if
end if;
return result
end proc;
readArrayPrivateLength(arrayphase) returns an Array’s private length. See also readLength, which can work on non-Array objects.
proc readArrayPrivateLength(arrayObjectphasePhase): Integer
lengthFloat64  readInstanceSlot(arrayarrayPrivate::“length”, phase);
note  length  {NaNf64+f64f64};
nRational  toRational(length);
note  n  Integer and 0  n  arrayLimit;
return n
end proc;
writeArrayPrivateLength(arraylengthphase) sets an Array’s private length to length after ensuring that length is between 0 and arrayLimit inclusive. See also writeLength, which can work on non-Array objects.
proc writeArrayPrivateLength(arrayObjectlengthIntegerphase: {run})
if length < 0 or length > arrayLimit then
throw a RangeError exception — array length out of range
end if;
Evaluate dotWrite(array, {arrayPrivate::“length”}, lengthf64phase) and ignore its result
end proc;
proc multinameToUnsignedInteger(multinameMultiname): IntegerOpt
if |multiname 1 then return none end if;
qnameQualifiedName  the one element of multiname;
if qname.namespace  public then return none end if;
nameString  qname.id;
if name  [] then
if name = “0” then return 0
elsif name[0]  ‘0’ and (every ch  name satisfies ch  {‘0’ ... ‘9’}) then
end if
end if;
return none
end proc;
proc initArray(thisSimpleInstanceargsObject[], phase: {run})
if |args| = 1 then
argObject  args[0];
if arg  GeneralNumber then
lengthIntegerOpt  checkInteger(arg);
if length = none then
throw a RangeError exception — array length must be an integer
end if;
Evaluate writeArrayPrivateLength(thislengthphase) and ignore its result;
return
end if
end if;
iInteger  0;
for each arg  args do
Evaluate indexWrite(thisiargphase) and ignore its result;
i  i + 1
end for each;
note  The call to indexWrite above also set the array’s length to i.
end proc;
proc Array_getLength(thisObjectphasePhase): Float64
note  is(thisArray) because this getter cannot be extracted from the Array class.
note  An array’s length is mutable, so reading it will throw ConstantError when phase = compile.
return readInstanceSlot(thisarrayPrivate::“length”, phase)
end proc;
proc Array_setLength(thisObjectlengthObjectphasePhase)
note  is(thisArray) because this setter cannot be extracted from the Array class.
if phase = compile then
throw a ConstantError exception — an array’s length cannot be set from a constant expression
end if;
newLengthIntegerOpt  checkInteger(objectToGeneralNumber(lengthphase));
if newLength = none or newLength < 0 or newLength > arrayLimit then
throw a RangeError exception — array length out of range or not an integer
end if;
oldLengthInteger  readArrayPrivateLength(thisphase);
if newLength < oldLength then
note  Delete all indexed properties greater than or equal to the new length
proc qnameInDeletedRange(qnameQualifiedName): Boolean
iIntegerOpt  multinameToUnsignedInteger({qname});
return i  none and newLength  i < oldLength
end proc;
this.localBindings  {b | b  this.localBindings such that not qnameInDeletedRange(b.qname)}
end if;
Evaluate writeArrayPrivateLength(thisnewLengthphase) and ignore its result
end proc;
ArrayPrototypeSimpleInstancenew SimpleInstancelocalBindings: {
stdConstBinding(public::“constructor”, ClassArray),
stdFunction(public::“toString”, Array_toString, 0),
stdFunction(public::“toLocaleString”, Array_toLocaleString, 0),
stdFunction(public::“concat”, Array_concat, 1),
stdFunction(public::“join”, Array_join, 1),
stdFunction(public::“pop”, Array_pop, 0),
stdFunction(public::“push”, Array_push, 1),
stdFunction(public::“reverse”, Array_reverse, 0),
stdFunction(public::“shift”, Array_shift, 0),
stdFunction(public::“slice”, Array_slice, 2),
stdFunction(public::“sort”, Array_sort, 1),
stdFunction(public::“splice”, Array_splice, 2),
stdFunction(public::“unshift”, Array_unshift, 1)},
archetypeObjectPrototype, sealedprototypesSealed, typeObject, slots: {}, callnone, constructnone, envnone;
proc Array_toString(thisObjectfSimpleInstanceargsObject[], phasePhase): String
if phase = compile then
throw a ConstantError exception — toString cannot be called on an Array from a constant expression
end if;
note  This function is generic and can be applied even if this is not an Array.
note  This function ignores any arguments passed to it in args.
return internalJoin(this, “,”, phase)
end proc;
proc Array_toLocaleString(thisObjectfSimpleInstanceargsObject[], phasePhase): String
if phase = compile then
throw a ConstantError exception — toLocaleString cannot be called on an Array from a constant expression
end if;
note  This function is generic and can be applied even if this is not an Array.
note  This function passes any arguments passed to it in args to toLocaleString applied to the elements of the array.
separatorString  the list-separator string appropriate for the host’s current locale, derived in an implementation-defined way;
lengthInteger  readLength(thisphase);
resultString  “”;
iInteger  0;
while i  length do
eltObjectOpt  indexRead(thisiphase);
if elt  {undefinednullnonethen
toLocaleStringMethodObject  dotRead(elt, {public::“toLocaleString”}, phase);
sObject  call(elttoLocaleStringMethodargsphase);
if s  Char16  String then
throw a TypeError exception — toLocaleString should return a string
end if;
result  result  toString(s)
end if;
i  i + 1;
if i  length then result  result  separator end if
end while;
return result
end proc;
proc Array_concat(thisObjectfSimpleInstanceargsObject[], phasePhase): Object
if phase = compile then
throw a ConstantError exception — concat cannot be called from a constant expression
end if;
note  This function is generic and can be applied even if this is not an Array.
constituentsObject[]  [this]  args;
arrayObject  construct(Array[]phase);
iInteger  0;
for each o  constituents do
if is(oArraythen
oLengthInteger  readLength(ophase);
kInteger  0;
while k  oLength do
eltObjectOpt  indexRead(okphase);
if elt  none then
Evaluate indexWrite(arrayieltphase) and ignore its result
end if;
k  k + 1;
i  i + 1
end while
else Evaluate indexWrite(arrayiophase) and ignore its result; i  i + 1
end if
end for each;
Evaluate writeArrayPrivateLength(arrayiphase) and ignore its result;
return array
end proc;
proc Array_join(thisObjectfSimpleInstanceargsObject[], phasePhase): String
if phase = compile then
throw a ConstantError exception — join cannot be called from a constant expression
end if;
note  This function is generic and can be applied even if this is not an Array.
if |args| > 1 then
throw an ArgumentError exception — at most one argument can be supplied
end if;
argObject  defaultArg(args, 0, undefined);
separatorString  “,”;
if arg  undefined then separator  objectToString(argphaseend if;
return internalJoin(thisseparatorphase)
end proc;
proc internalJoin(thisObjectseparatorStringphase: {run}): String
lengthInteger  readLength(thisphase);
resultString  “”;
iInteger  0;
while i  length do
eltObjectOpt  indexRead(thisiphase);
if elt  {undefinednullnonethen
result  result  objectToString(eltphase)
end if;
i  i + 1;
if i  length then result  result  separator end if
end while;
return result
end proc;
proc Array_pop(thisObjectfSimpleInstanceargsObject[], phasePhase): Object
if phase = compile then
throw a ConstantError exception — pop cannot be called from a constant expression
end if;
note  This function is generic and can be applied even if this is not an Array.
if |args 0 then throw an ArgumentError exception — no arguments can be supplied
end if;
lengthInteger  readLength(thisphase);
resultObject  undefined;
if length  0 then
length  length – 1;
eltObjectOpt  indexRead(thislengthphase);
if elt  none then
result  elt;
Evaluate indexWrite(thislengthnonephase) and ignore its result
end if
end if;
Evaluate writeLength(thislengthphase) and ignore its result;
return result
end proc;
proc Array_push(thisObjectfSimpleInstanceargsObject[], phasePhase): Object
if phase = compile then
throw a ConstantError exception — push cannot be called from a constant expression
end if;
note  This function is generic and can be applied even if this is not an Array.
lengthInteger  readLength(thisphase);
for each arg  args do
Evaluate indexWrite(thislengthargphase) and ignore its result;
length  length + 1
end for each;
Evaluate writeLength(thislengthphase) and ignore its result;
return lengthf64
end proc;
proc Array_reverse(thisObjectfSimpleInstanceargsObject[], phasePhase): Object
if phase = compile then
throw a ConstantError exception — reverse cannot be called from a constant expression
end if;
note  This function is generic and can be applied even if this is not an Array.
if |args 0 then throw an ArgumentError exception — no arguments can be supplied
end if;
lengthInteger  readLength(thisphase);
loInteger  0;
hiInteger  length – 1;
while lo < hi do
loEltObjectOpt  indexRead(thislophase);
hiEltObjectOpt  indexRead(thishiphase);
Evaluate indexWrite(thislohiEltphase) and ignore its result;
Evaluate indexWrite(thishiloEltphase) and ignore its result;
lo  lo + 1;
hi  hi – 1
end while;
return this
end proc;
proc Array_shift(thisObjectfSimpleInstanceargsObject[], phasePhase): Object
if phase = compile then
throw a ConstantError exception — shift cannot be called from a constant expression
end if;
note  This function is generic and can be applied even if this is not an Array.
if |args 0 then throw an ArgumentError exception — no arguments can be supplied
end if;
lengthInteger  readLength(thisphase);
resultObject  undefined;
if length  0 then
eltObjectOpt  indexRead(this, 0, phase);
if elt  none then result  elt end if;
iInteger  1;
while i  length do
elt  indexRead(thisiphase);
Evaluate indexWrite(thisi – 1, eltphase) and ignore its result;
i  i + 1
end while;
length  length – 1;
Evaluate indexWrite(thislengthnonephase) and ignore its result
end if;
Evaluate writeLength(thislengthphase) and ignore its result;
return result
end proc;
proc Array_slice(thisObjectfSimpleInstanceargsObject[], phasePhase): Object
if phase = compile then
throw a ConstantError exception — slice cannot be called on an Array from a constant expression
end if;
note  This function is generic and can be applied even if this is not an Array.
if |args| > 2 then
throw an ArgumentError exception — at most two arguments can be supplied
end if;
lengthInteger  readLength(thisphase);
startArgObject  defaultArg(args, 0, +zerof64);
endArgObject  defaultArg(args, 1, +f64);
startInteger  pinExtendedInteger(objectToExtendedInteger(startArgphase), lengthtrue);
endInteger  pinExtendedInteger(objectToExtendedInteger(endArgphase), lengthtrue);
return makeArraySlice(thisstartendphase)
end proc;
proc makeArraySlice(arrayObjectstartIntegerendIntegerphase: {run}): Object
sliceObject  construct(Array[]phase);
iInteger  start;
jInteger  0;
while i < end do
eltObjectOpt  indexRead(arrayiphase);
Evaluate indexWrite(slicejeltphase) and ignore its result;
i  i + 1;
j  j + 1
end while;
Evaluate writeLength(slicejphase) and ignore its result;
return slice
end proc;
proc Array_sort(thisObjectfSimpleInstanceargsObject[], phasePhase): Object
if phase = compile then
throw a ConstantError exception — sort cannot be called from a constant expression
end if;
note  This function is generic and can be applied even if this is not an Array.
if |args| > 1 then
throw an ArgumentError exception — at most one argument can be supplied
end if;
Evaluate ???? and ignore its result
end proc;
proc Array_splice(thisObjectfSimpleInstanceargsObject[], phasePhase): Object
if phase = compile then
throw a ConstantError exception — splice cannot be called from a constant expression
end if;
note  This function is generic and can be applied even if this is not an Array.
if |args| < 2 then
throw an ArgumentError exception — at least two arguments must be supplied
end if;
lengthInteger  readLength(thisphase);
startArgObject  defaultArg(args, 0, +zerof64);
deleteCountArgObject  defaultArg(args, 1, +zerof64);
startInteger  pinExtendedInteger(objectToExtendedInteger(startArgphase), lengthtrue);
deleteCountInteger  pinExtendedInteger(objectToExtendedInteger(deleteCountArgphase), length – start, false);
deletedSliceObject  makeArraySlice(thisstartstart + deleteCountphase);
newEltsObject[]  args[2 ...];
newEltCountInteger  |newElts|;
countDiffInteger  newEltCount – deleteCount;
iInteger;
if countDiff < 0 then
i  start + deleteCount;
while i  length do
eltObjectOpt  indexRead(thisiphase);
Evaluate indexWrite(thisi + countDiffeltphase) and ignore its result;
i  i + 1
end while;
i  0;
while i  countDiff do
i  i – 1;
Evaluate indexWrite(thislength + inonephase) and ignore its result
end while
elsif countDiff > 0 then
i  length;
while i  start + deleteCount do
i  i – 1;
eltObjectOpt  indexRead(thisiphase);
Evaluate indexWrite(thisi + countDiffeltphase) and ignore its result
end while
end if;
Evaluate writeLength(thislength + countDiffphase) and ignore its result;
i  start;
for each arg  newElts do
Evaluate indexWrite(thisiargphase) and ignore its result;
i  i + 1
end for each;
return deletedSlice
end proc;
proc Array_unshift(thisObjectfSimpleInstanceargsObject[], phasePhase): Float64
if phase = compile then
throw a ConstantError exception — unshift cannot be called from a constant expression
end if;
note  This function is generic and can be applied even if this is not an Array.
iInteger  readLength(thisphase);
nArgsInteger  |args|;
newLengthInteger  nArgs + i;
if nArgs = 0 then
At the implementation’s discretion, either do nothing or return newLengthf64
end if;
Evaluate writeLength(thisnewLengthphase) and ignore its result;
while i  0 do
i  i – 1;
eltObjectOpt  indexRead(thisiphase);
Evaluate indexWrite(thisi + nArgseltphase) and ignore its result
end while;
for each arg  args do
Evaluate indexWrite(thisiargphase) and ignore its result;
i  i + 1
end for each;
return newLengthf64
end proc;

Namespace

NamespaceClassnew ClasslocalBindings: {}, instanceProperties: {}, superObject, prototypeNamespacePrototype, completetrue, name: “Namespace”, typeofString: “namespace”, dynamicfalse, finaltrue, defaultValuenull, defaultHinthintString, hasPropertyordinaryHasProperty, bracketReadordinaryBracketRead, bracketWriteordinaryBracketWrite, bracketDeleteordinaryBracketDelete, readordinaryRead, writeordinaryWrite, deleteordinaryDelete, enumerateordinaryEnumerate, callordinaryCall, constructconstructNamespace, initnone, isordinaryIs, coerceordinaryCoerce;
proc constructNamespace(cClassargsObject[], phasePhase): Namespace
note  This function can be used in a constant expression if its argument is a string.
if |args| > 1 then
throw an ArgumentError exception — at most one argument can be supplied
end if;
argObject  defaultArg(args, 0, undefined);
if arg  Null  Undefined then
if phase = compile then
throw a ConstantError exception — a constant expression cannot construct new anonymous namespaces
end if;
return new Namespacename: “anonymous
elsif arg  Char16  String then
nameString  toString(arg);
if name = “” then return public
elsif some ns  namedNamespaces satisfies ns.name = name then return ns
else
ns2Namespace  new Namespacenamename;
namedNamespaces  namedNamespaces  {ns2};
return ns2
end if
else throw a TypeError exception
end if
end proc;
namedNamespacesNamespace{}  {};
NamespacePrototypeSimpleInstancenew SimpleInstancelocalBindings: {
stdFunction(public::“toString”, Namespace_toString, 0),
stdReserve(public::“valueOf”, ObjectPrototype)},
archetypeObjectPrototype, sealedprototypesSealed, typeObject, slots: {}, callnone, constructnone, envnone;
proc Namespace_toString(thisObjectfSimpleInstanceargsObject[], phasePhase): String
note  This function does not check phase and therefore can be used in a constant expression.
note  This function ignores any arguments passed to it in args.
if this  Namespace then throw a TypeError exception end if;
return this.name
end proc;

Attribute

AttributeClassnew ClasslocalBindings: {}, instanceProperties: {}, superObject, prototypeObjectPrototype, completetrue, name: “Attribute”, typeofString: “object”, dynamicfalse, finaltrue, defaultValuenull, defaultHinthintString, hasPropertyordinaryHasProperty, bracketReadordinaryBracketRead, bracketWriteordinaryBracketWrite, bracketDeleteordinaryBracketDelete, readordinaryRead, writeordinaryWrite, deleteordinaryDelete, enumerateordinaryEnumerate, calldummyCall, constructdummyConstruct, initnone, isordinaryIs, coerceordinaryCoerce;

Date

DateClassnew ClasslocalBindings: {}, instanceProperties: {}, superObject, prototypeDatePrototype, completetrue, name: “Date”, typeofString: “object”, dynamictrue, finaltrue, defaultValuenull, defaultHinthintString, hasPropertyordinaryHasProperty, bracketReadordinaryBracketRead, bracketWriteordinaryBracketWrite, bracketDeleteordinaryBracketDelete, readordinaryRead, writeordinaryWrite, deleteordinaryDelete, enumerateordinaryEnumerate, calldummyCall, constructdummyConstruct, initnone, isordinaryIs, coerceordinaryCoerce;
DatePrototypeSimpleInstancenew SimpleInstancelocalBindings: {}, archetypeObjectPrototype, sealedprototypesSealed, typeObject, slots: {}, callnone, constructnone, envnone;

RegExp

RegExpClassnew ClasslocalBindings: {}, instanceProperties: {}, superObject, prototypeRegExpPrototype, completetrue, name: “RegExp”, typeofString: “object”, dynamictrue, finaltrue, defaultValuenull, defaultHinthintNumber, hasPropertyordinaryHasProperty, bracketReadordinaryBracketRead, bracketWriteordinaryBracketWrite, bracketDeleteordinaryBracketDelete, readordinaryRead, writeordinaryWrite, deleteordinaryDelete, enumerateordinaryEnumerate, calldummyCall, constructdummyConstruct, initnone, isordinaryIs, coerceordinaryCoerce;
RegExpPrototypeSimpleInstancenew SimpleInstancelocalBindings: {}, archetypeObjectPrototype, sealedprototypesSealed, typeObject, slots: {}, callnone, constructnone, envnone;

Class

ClassClassnew ClasslocalBindings: {}, instanceProperties: {classPrototypeGetter}, superObject, prototypeClassPrototype, completetrue, name: “Class”, typeofString: “function”, dynamicfalse, finaltrue, defaultValuenull, defaultHinthintString, hasPropertyordinaryHasProperty, bracketReadordinaryBracketRead, bracketWriteordinaryBracketWrite, bracketDeleteordinaryBracketDelete, readordinaryRead, writeordinaryWrite, deleteordinaryDelete, enumerateordinaryEnumerate, calldummyCall, constructdummyConstruct, initnone, isordinaryIs, coerceordinaryCoerce;
classPrototypeGetterInstanceGetternew InstanceGettermultiname: {public::“prototype”}, finaltrue, enumerablefalse, callClass_prototype;
proc Class_prototype(thisObjectphasePhase): Object
note  this  Class because this getter cannot be extracted from the Class class.
prototypeObjectOpt  this.prototype;
if prototype = none then return undefined else return prototype end if
end proc;
ClassPrototypeSimpleInstancenew SimpleInstancelocalBindings: {
stdConstBinding(public::“constructor”, ClassClass),
stdFunction(public::“toString”, Class_toString, 0),
stdReserve(public::“valueOf”, ObjectPrototype),
stdConstBinding(public::“length”, Number, 1f64)},
archetypeObjectPrototype, sealedprototypesSealed, typeObject, slots: {}, callnone, constructnone, envnone;
proc Class_toString(thisObjectfSimpleInstanceargsObject[], phasePhase): String
note  This function does not check phase and therefore can be used in a constant expression.
note  This function ignores any arguments passed to it in args.
cClass  objectToClass(this);
return “[class ”  c.name  “]
end proc;

Function

FunctionClassnew ClasslocalBindings: {}, instanceProperties: {ivarFunctionLength}, superObject, prototypeFunctionPrototype, completetrue, name: “Function”, typeofString: “function”, dynamicfalse, finaltrue, defaultValuenull, defaultHinthintString, hasPropertyordinaryHasProperty, bracketReadordinaryBracketRead, bracketWriteordinaryBracketWrite, bracketDeleteordinaryBracketDelete, readordinaryRead, writeordinaryWrite, deleteordinaryDelete, enumerateordinaryEnumerate, calldummyCall, constructdummyConstruct, initnone, isordinaryIs, coerceordinaryCoerce;
ivarFunctionLengthInstanceVariablenew InstanceVariablemultiname: {public::“length”}, finaltrue, enumerablefalse, typeNumber, defaultValuenone, immutabletrue;
FunctionPrototypeSimpleInstancenew SimpleInstancelocalBindings: {}, archetypeObjectPrototype, sealedprototypesSealed, typeObject, slots: {}, callnone, constructnone, envnone;

PrototypeFunction

PrototypeFunctionClassnew ClasslocalBindings: {}, instanceProperties: {new InstanceVariablemultiname: {public::“prototype”}, finaltrue, enumerablefalse, typeObject, defaultValueundefined, immutablefalse}, superFunction, prototypeFunctionPrototype, completetrue, name: “Function”, typeofString: “function”, dynamictrue, finaltrue, defaultValuenull, defaultHinthintString, hasPropertyordinaryHasProperty, bracketReadordinaryBracketRead, bracketWriteordinaryBracketWrite, bracketDeleteordinaryBracketDelete, readordinaryRead, writeordinaryWrite, deleteordinaryDelete, enumerateordinaryEnumerate, calldummyCall, constructdummyConstruct, initnone, isordinaryIs, coerceordinaryCoerce;

Package

PackageClassnew ClasslocalBindings: {}, instanceProperties: {}, superObject, prototypeObjectPrototype, completetrue, name: “Package”, typeofString: “object”, dynamictrue, finaltrue, defaultValuenull, defaultHinthintString, hasPropertyordinaryHasProperty, bracketReadordinaryBracketRead, bracketWriteordinaryBracketWrite, bracketDeleteordinaryBracketDelete, readordinaryRead, writeordinaryWrite, deleteordinaryDelete, enumerateordinaryEnumerate, calldummyCall, constructdummyConstruct, initnone, isordinaryIs, coerceordinaryCoerce;

Error

ErrorClassnew ClasslocalBindings: {}, instanceProperties: {
new InstanceVariablemultiname: {public::“name”}, finalfalse, enumerabletrue, typeString, defaultValuenull, immutablefalse,
new InstanceVariablemultiname: {public::“message”}, finalfalse, enumerabletrue, typeString, defaultValuenull, immutablefalse},
superObject, prototypeErrorPrototype, completetrue, name: “Error”, typeofString: “object”, dynamictrue, finalfalse, defaultValuenull, defaultHinthintNumber, hasPropertyordinaryHasProperty, bracketReadordinaryBracketRead, bracketWriteordinaryBracketWrite, bracketDeleteordinaryBracketDelete, readordinaryRead, writeordinaryWrite, deleteordinaryDelete, enumerateordinaryEnumerate, callcallError, constructordinaryConstruct, initinitError, isordinaryIs, coerceordinaryCoerce;
proc callError(thisObjectcClassargsObject[], phasePhase): Object
if |args| > 1 then
throw an ArgumentError exception — at most one argument can be supplied
end if;
argObject  defaultArg(args, 0, undefined);
if arg = null or is(argErrorthen return arg
else return construct(cargsphase)
end if
end proc;
proc initError(thisSimpleInstanceargsObject[], phase: {run})
if |args| > 1 then
throw an ArgumentError exception — at most one argument can be supplied
end if;
nameString  Null  dotRead(ErrorPrototype, {public::“name”}, phase);
Evaluate dotWrite(this, {public::“name”}, namephase) and ignore its result;
argObject  defaultArg(args, 0, undefined);
messageString  Null;
if arg = undefined then message  dotRead(ErrorPrototype, {public::“message”}, phase)
else message  objectToString(argphase)
end if;
Evaluate dotWrite(this, {public::“message”}, messagephase) and ignore its result
end proc;
ErrorPrototypeSimpleInstancenew SimpleInstancelocalBindings: {
stdConstBinding(public::“constructor”, ClassError),
stdFunction(public::“toString”, Error_toString, 1),
stdVarBinding(public::“name”, String, “Error”),
stdVarBinding(public::“message”, String, an implementation-defined string)},
archetypeObjectPrototype, sealedprototypesSealed, typeObject, slots: {}, callnone, constructnone, envnone;
proc Error_toString(thisObjectfSimpleInstanceargsObject[], phasePhase): String
if phase = compile then
throw a ConstantError exception — toString cannot be called on an Error from a constant expression
end if;
note  This function ignores any arguments passed to it in args.
errObject  coerceNonNull(thisError);
nameString  Null  dotRead(err, {public::“name”}, phase);
messageString  Null  dotRead(err, {public::“message”}, phase);
return an implementation-defined string derived from name, message, and optionally other properties of err
end proc;
proc systemError(eClassmsgString  Undefined): Object
return construct(e[msg]run)
end proc;

Error Subclasses

proc makeBuiltInErrorSubclass(nameString): Class
proc call(thisObjectcClassargsObject[], phasePhase): Object
if |args| > 1 then
throw an ArgumentError exception — at most one argument can be supplied
end if;
argObject  defaultArg(args, 0, undefined);
if arg = null or is(argErrorthen return coerce(argc)
else return construct(cargsphase)
end if
end proc;
cClass  new ClasslocalBindings: {}, instanceProperties: {}, superError, completefalse, namename, typeofString: “object”, dynamictrue, finalfalse, defaultValuenull, defaultHinthintNumber, hasPropertyError.hasProperty, bracketReadError.bracketRead, bracketWriteError.bracketWrite, bracketDeleteError.bracketDelete, readError.read, writeError.write, deleteError.delete, enumerateError.enumerate, callcall, constructordinaryConstruct, initnone, isordinaryIs, coerceordinaryCoerce;
prototypeSimpleInstance  new SimpleInstancelocalBindings: {
stdConstBinding(public::“constructor”, Classc),
stdVarBinding(public::“name”, Stringname),
stdVarBinding(public::“message”, String, an implementation-defined string)},
archetypeErrorPrototype, sealedprototypesSealed, typeObject, slots: {}, callnone, constructnone, envnone;
proc init(thisSimpleInstanceargsObject[], phase: {run})
if |args| > 1 then
throw an ArgumentError exception — at most one argument can be supplied
end if;
name2String  Null  dotRead(prototype, {public::“name”}, phase);
Evaluate dotWrite(this, {public::“name”}, name2phase) and ignore its result;
argObject  defaultArg(args, 0, undefined);
messageString  Null;
if arg = undefined then message  dotRead(prototype, {public::“message”}, phase)
else message  objectToString(argphase)
end if;
Evaluate dotWrite(this, {public::“message”}, messagephase) and ignore its result
end proc;
c.prototype  prototype;
c.init  init;
c.complete  true;
return c
end proc;
ArgumentErrorClass = makeBuiltInErrorSubclass(“ArgumentError”);
AttributeErrorClass = makeBuiltInErrorSubclass(“AttributeError”);
ConstantErrorClass = makeBuiltInErrorSubclass(“ConstantError”);
DefinitionErrorClass = makeBuiltInErrorSubclass(“DefinitionError”);
EvalErrorClass = makeBuiltInErrorSubclass(“EvalError”);
RangeErrorClass = makeBuiltInErrorSubclass(“RangeError”);
ReferenceErrorClass = makeBuiltInErrorSubclass(“ReferenceError”);
SyntaxErrorClass = makeBuiltInErrorSubclass(“SyntaxError”);
TypeErrorClass = makeBuiltInErrorSubclass(“TypeError”);
UninitializedErrorClass = makeBuiltInErrorSubclass(“UninitializedError”);
URIErrorClass = makeBuiltInErrorSubclass(“URIError”);

ECMAScript 4 Netscape Proposal
Rationale
previousupnext

Tuesday, January 28, 2003


This chapter discusses the decisions made in desigining ECMAScript 4. Rationales are presented together with descriptions of other alternatives that were/are considered. Currently outstanding issues are in red.


ECMAScript 4 Netscape Proposal
Rationale
Execution Model
previousupnext

Tuesday, September 10, 2002

This page is somewhat out of date.

Introduction

When does a declaration (of a value, function, type, class, method, pragma, etc.) take effect? When are expressions evaluated? The answers to these questions distinguish among major kinds of programming languages. Let’s consider the following function definition in a language with C++ or Java-like syntax:

gadget f(widget x) {
  if ((gizmo)(x) != null)
    return (gizmo)(x);
  return x.owner;
}

In a static language such as Java or C++, all type expressions are evaluated at compile time. Thus, in this example widget and gadget would be evaluated at compile time. If gizmo were a type, then it too would be evaluated at compile time ((gizmo)(x) would become a type cast). Note that we must be able to statically distinguish identifiers used for variables from identifiers used for types so we can decide whether (gizmo)(x) is a one-argument function call (in which case gizmo would be evaluated at run time) or a type cast (in which case gizmo would be evaluated at compile time). In most cases, in a static language a declaration is visible throughout its enclosing scope, although there are exceptions that have been deemed too complicated for a compiler to handle such as the following C++:

typedef int *x;

class foo {
  typedef x *y;
  typedef char *x;
}

Many dynamic languages can construct, evaluate, and manipulate type expressions at run time. Some dynamic languages (such as Common Lisp) distinguish between compile time and run time and provide constructs (eval-when) to evaluate expressions early. The simplest dynamic languages (such as Scheme) process input in a single pass and do not distinguish between compile time and run time. If we evaluated the above function in such a simple language, widget and gadget would be evaluated at the time the function is called.

Challenges

ECMAScript is a scripting language. Many programmers wish to write ECMAScript scripts embedded in web pages that work in a variety of environments. Some of these environments may provide libraries that a script would like to use, while on other environments the script may have to emulate those libraries. Let’s take a look at an example of something one would expect to be able to easily do in a scripting language:

Bob is writing a script for a web page that wants to take advantage of an optional package MacPack that is present on some environments (Macintoshes) but not on others. MacPack provides a class HyperWindoid from which Bob wants to subclass his own class BobWindoid. On other platforms Bob has to define an emulation class BobWindoid' that is implemented differently from BobWindoid — it has a different set of private methods and fields. There also is a class WindoidGuide in Bob’s package; the code and method signatures of classes BobWindoid and BobWindoid' refer to objects of type WindoidGuide, and class WindoidGuide’s code refers to objects of type BobWindoid (or BobWindoid' as appropriate).

Were ECMAScript to use a dynamic execution model (described below), declarations take effect only when executed, and Bob can implement his package as shown below.

class WindoidGuide; // forward declaration

if (onMac()) {
  import "MacPack";

  global class BobWindoid extends HyperWindoid {
    private var x;
    var g:WindoidGuide;

    private function speck() {...};
    public function zoom(a:WindoidGuide, uncle:HyperWindoid = null):WindoidGuide {...};
  }
} else {
  // emulation class BobWindoid'
  global class BobWindoid {
    private var i:Integer, j:Integer;
    var g:WindoidGuide;

    private function advertise(h:WindoidGuide):WindoidGuide {...};
    private function subscribe(h:WindoidGuide):WindoidGuide {...};
    public function zoom(a:WindoidGuide):WindoidGuide {...};
  }
}

class WindoidGuide {
  var currentWindoid:BobWindoid;

  function introduce(arg:BobWindoid):BobWindoid {...};
}

On the other hand, if the language were static (meaning that types are compile-time expressions), Bob would run into problems. How could he declare the two alternatives for the class BobWindoid?

Bob’s first thought was to split his package into three HTML SCRIPT tags (containing BobWindoid, BobWindoid', and WindoidGuide) and turn one of the first two off depending on the platform. Unfortunately this doesn’t work because he gets type errors if he separates the definition of class BobWindoid (or BobWindoid') from the definition of WindoidGuide because these classes mutually refer to each other. Furthermore, Bob would like to share the script among many pages, so he’d like to have the entire script in a single BobUtilities.js file.

Note that this problem would be newly introduced by ECMAScript 4 if it were to evaluate type expressions at compile time. ECMAScript 3 does not suffer from this problem because it does not have a concept of evaluating an expression at compile time, and it is relatively easy to conditionally define a class (which is merely a function) by declaring a single global variable g and conditionally assigning either one or another anonymous function to it.

There exist other alternatives in between the dynamic execution model and the static model that also solve Bob’s problem. One of them is described at the end of this chapter.

Dynamic Execution Model

In a pure dynamic execution model the entire program is processed in one pass. Declarations take effect only when they are executed. A declaration that is never executed is ignored. Scheme follows this model, as did early versions of Visual Basic.

The dynamic execution model considerably simplifies the language and allows an interpreter to treat programs read from a file identically to programs typed in via an interactive console. Also, a dynamic execution model interpreter or just-in-time compiler may start to execute a script even before it has finished downloading all of it.

One of the most significant advantages of the dynamic execution model is that it allows ECMAScript 4 scripts to turn parts of themselves on and off based on dynamically obtained information. For example, a script or library could define additional functions and classes if it runs on an environment that provides a CSS unit arithmetic library while still working on environments that do not.

The dynamic execution model requires identifiers naming functions and variables to be defined before they are used. A use occurs when an identifier is read, written, or called, at which point that identifier is resolved to a variable or a function according to the scoping rules. A reference from within a control statement such as if and while located outside a function is resolved only when execution reaches the reference. References from within the body of a function are resolved only after the function is called; for efficiency, an implementation is allowed to resolve all references within a function or method that does not contain eval at the first time the function is called.

According to these rules, the following program is correct and would print 7:

function f(a:Integer):Integer {
  return a+b;
}

var b:Integer = 4;
print(f(3));

Assuming that variable b is predefined by the host if featurePresent is true, this program would also work:

function f(a:Integer):Integer {
  return a+b;
}

if (!featurePresent) {
  var b:Integer = 4;
}

print(f(3));

On the other hand, the following program would produce an error because f is referenced before it is defined:

print(f(3));

function f(a:Integer):Integer {
  return a*2;
}

Defining mutually recursive functions is not a problem as long as one defines all of them before calling them.

Hybrid Execution Model

ECMAScript 3 does not follow the pure dynamic execution model, and, for reasons of compatibility, ECMAScript 4 strays from that model as well, adopting a hybrid execution model instead. Specifically, ECMAScript 4 inherits the following static execution model aspects from ECMAScript 3:

  • Variable declarations of variables at the global scope cause the variables to be created at the time the program is entered rather than at the time the declaractions are evaluated.
  • Variable declarations of local variables inside a function cause the variables to be created at the time the function is entered rather than at the time the declaractions are evaluated.
  • Unless specified otherwise, declarations of functions at the global scope cause the functions to be created at the time the program is entered rather than at the time the declaractions are evaluated.
  • Unless specified otherwise, declarations of functions nested inside a function cause the functions to be created at the time the function is entered rather than at the time the declaractions are evaluated.

In addition to the above, the evaluation of class declarations has special provisions for delayed evaluation to allow mutually-referencing classes.

The second condition above allows the following program to work in ECMAScript 4:

const b:String = "Bee";

function square(a:Integer):Integer {
  b = a;   // Refers to local b defined below, not global b
  return b*a;
  var b:Integer;
}

While allowed, using variables ahead of declaring them, such as in the above example, is considered bad style and may generate a warning.

The third condition above makes the last example from the pure execution model section work:

print(f(3));

function f(a:Integer):Integer {
  return a*2;
}

Again, actually calling a function at the top level before declaring it is considered bad style and may generate a warning. It also will not work with classes.

Compiling The Dynamic Execution Model

Perhaps the easiest way to compile a script under the dynamic execution model is to accumulate function definitions unprocessed and compile them only when they are first called. Many JITs do this anyway because this lets them avoid the overhead of compiling functions that are never called. This process does not impose any more of an overhead than the static model would because under the static model the compiler would need to either scan the source code twice or save all of it unprocessed during the first pass for processing in the second pass.

Compiling a dynamic execution model script off-line also does not present special difficulties as long as eval is restricted to not introduce additional declarations that shadow existing ones (if eval is allowed to do this, it would present problems for any execution model, including the static one). Under the dynamic execution model, once the compiler has reached the end of a scope it can assume that that scope is complete; at that point all identifiers inside that scope can be resolved to the same extent that they would be in the static model.

Conditional Compilation Alternative

Bob’s problem could also be solved by using conditional compilation similar in spirit to C’s preprocessor. If we do this, we have to ask about how expressive the conditional compilation meta-language should be. C’s preprocessor is too weak. In ECMAScript applications we’d often find that we need the full power of ECMAScript so that we can inspect the DOM, the environment, etc. when deciding how to control compilation. Besides, using ECMAScript as the meta-language would reduce the number of languages that a programmer would have to learn.

Here’s one sketch of how this could be done:

  • Execution of a script occurs in a series of passes. The last one is called run time. It is preceded by a compile time pass, which is preceded (if necessary) by a compile compile time pass, which is preceded (if necessary) by a compile compile compile time pass, etc. Having anything beyond a compile compile time pass is very rare, but rapid-development tools could generate such code.
  • All definitions made in a previous pass (such as compile time) are also visible in a later pass (such as run time). If we define a type to also be a function that casts its argument to it type, this allows an implementation to not worry about whether (x)(y) is a function call of function x or a cast of y to type x.
  • TypeExpressions are evaluated at compile time. These appear as types of variables, constants, and fields; argument and result types of functions and methods; and superclasses.
  • Declarations are accumulated at compile time. Variables are declared, but variable initializers are not evaluated. Functions and classes are declared and defined at compile time.
  • One can lift a Block from run time to compile time by preceding the block with a # symbol. For example, #{var x:int = 3} defines a compile-time constant x and initializes it to 3. One can also lift a var, const, or function declaration directly by preceding it with a # symbol, so #var x:int = 3; would accomplish the same thing.
  • Any TypeExpressions in blocks or statements lifted to compile time are evaluated at compile compile time. int in the preceding example is such a TypeExpression.
  • Any block or statement that is lifted twice (as in #{#var x:int = 3}) is evaluated at compile compile time, and so forth.
  • The compile time pass can selectively exclude source code statements from the run time pass using the construct below. If the first Expression is true, the first Statements list is included in the run time pass. Otherwise, if the second Expression is true, the second Statements list is included in the run time pass, and so on.
    # if ( Expression ) Statements [# else if ( Expression ) Statements] ... [# else Statements# end if
  • Unlike in C, the Statements above must have balanced parentheses, braces, brackets, etc. Also, unlike in C, preprocessing is not line-oriented; #’s can appear anywhere on a line.
  • The compile compile time pass can use #if to conditionally exclude compile time code, etc.

Note that because variable initializers are not evaluated at compile time, one has to use #var a = int rather than var a = int to define an alias a for a type name int.

This sketch does not address many issues that would have to be resolved, such as how typed variables are handled after they are declared but before they are initialized (this problem doesn’t arise in the dynamic execution model), how the lexical scopes of the run time pass would interact with scoping of the compile time pass, etc.

Comparing the Dynamic Execution Model with Conditional Compilation

Both approaches solve Bob’s problem, but they differ in other areas. In the sequel "conditional compilation" refers to the conditional compilation alternative described above.

  • The dynamic execution model is simpler to describe and analyze.
  • The dynamic execution model is easier to learn for non-programmers.
  • Conditional compilation is more familar to C/C++ programmers.
  • Conditional compilation requires either a control meta-language or facilities for lifting execution from run to compile time. These often have unintended consequences (compile-time evaluation does not follow the same scopes as run-time evaluation) and have been a significant source of problems in Common Lisp.
  • Conditional compilation can more faithfully emulate ECMAScript 3 semantics because it allows a function to be called at the top level before it is defined.
  • Conditional compilation cannot guarantee that variables’ initializers have been run before the variables are referenced. The dynamic execution model guarantees that.
  • Conditional compilation does not allow one to use general prototype-based types because prototypes are often calculated at run time. The dynamic execution model allows these.
  • Conditional compilation does not support functors and templates without significant additional syntax. The dynamic execution model naturally extends to these.

Compiler Blocks

Another alternative execution model briefly considered but rejected is the idea of allowing compiler blocks. A compiler block has the syntax:

   compile { Statement ... Statement }

The compile attribute is a hint that the block may be (but does not have to be) evaluated early. The statements inside this block should depend only on each other, on the results of earlier compiler blocks, and on properties of the environment that are designated as being available early. Other than perhaps being evaluated early, compiler blocks respect all of the scope rules and semantics of the enclosing program. Any definitions introduced by a compiler block are saved and reintroduced at normal evaluation time. On the other hand, side effects may or may not be reintroduced at normal evaluation time, so compiler blocks should not rely on side effects.

compile is an attribute, so it may also be applied to individual definitions without enclosing them in a block.

As an example, after defining

compile var x = 2;

function f1() {
  compile {
    var y = 5;
    var x = 1;
    while (y) x *= y--;
  }
  return ++x;
}

function f2() {
  compile {
    var y = x;
  }
  return x+y;
}

the value of global x will still be 2, calling f1() will always return 121, and calling f2() will return 4. If the statement x=5 is then evaluated at the global level, f1() will still return 121 because it uses its own local x. On the other hand, calling f2() may return either 7 or 10 at the implementation’s discretion — 7 if the implementation evaluated the compile block early and saved the value of y or 10 if it didn’t. As this example illustrates, it is poor technique to define variables inside compiler blocks; constants are usually better.

A fully dynamic implementation of ECMAScript 4 may choose to ignore the compile attribute and evaluate all compiler blocks at normal evaluation time. A fully static implementation may require that all user-defined types and attributes be defined inside compiler blocks.

Should const definitions with simple constant expressions such as const four = 2+2 be treated as though they were implicitly compiler definitions (compile const four = 2+2)?


ECMAScript 4 Netscape Proposal
Rationale
Member Lookup
previousupnext

Friday, September 20, 2002

This page is somewhat out of date.

Introduction

There have been much discussion in the TC39 subgroup about the meaning of a member lookup operation. Numerous considerations intersect here.

We will express a general unqualified member lookup operation as a.b, where a is an expression and b is an identifier. We will also consider qualified member lookup operations and write them as a.n::b, where n is an expression that evaluates to some namespace. In almost all cases we will be interested in the dynamic type Td of a. In one scheme we will also consider the static type Ts of the expression a. If the language is sound, we will always have Td  Ts.

In the simplest approach, we treat an object as merely an association table of member names and member values. In this interpretation we simply look inside object a and check if there is a member named b. If there is, we return the member’s value; if not, we return undefined or signal an error.

There are a number of difficulties with this simple approach, and most object-oriented languages have not adopted it:

  • Members cannot be made private or internal.
  • Accidental clashes can occur when programming in the large.

Once we allow private or internal members, we must allow for the possibility that object a will have more than one member named b — abstraction considerations require that users of a class C not be aware of expose C’s private members, so, in particular, a user should be able to create a subclass D of C and add members to D without knowing the names of C’s private members. Both C++ and Java allow this. We must also allow for the possibility that object a will have a member named b but we are not allowed to access it. We will assume that access control is specified by lexical scoping, as is traditional in modern languages.

Desirable Criteria

Some of the criteria we would like the member lookup model to satisfy are:

  1. Safety. The lookup does not permit access to a private member outside the class where the member is defined, nor does it allow access to an internal member outside the package where the member is defined. Furthermore, if a class C accesses its private member m, a hostile subclass D of C cannot silently substitute a member m' that would masquerade as m inside C’s code.
  2. Abstraction. private and internal members are invisible outside their respective classes or packages. For programming in the large, a class can provide several public versions to its importers, and public members of more recent versions are invisible to importers of older versions. This is needed to provide robust libraries.
  3. Robustness. We can make any of the following program changes without having to restructure the program:
    1. Add valid type annotations to variables and functions.
    2. Change a member’s visibility to private, internal, or public, assuming, of course, that that member is not used outside its new visibility.
    3. Split a complicated expression statement e into several statements that compute subexpressions of e, store them in local variables, and then combine them to compute e. We should be able to do this without intimate knowledge of what e does or calls.
    4. Rename a member to a different name, assuming, of course, that the new name does not cause conflicts and that we fix up all references to that member.
  4. Namespace independence. If one class C has a member named m, this should not place restrictions on an unrelated class D having an unrelated member with the same name m.
  5. Compatibility. An ECMAScript 4 class should be usable from ECMAScript 3 code and ECMAScript 3 code minimally upgraded to ECMAScript 4 without having to restructure the latter code. Achieving compatibility should not require the ECMAScript 4 class itself to be restructured or give up any of the other desirable criteria. Code without type annotations works as expected.

Lookup Models

There are three main competing models for performing a general unqualified member lookup operation as a.b. Let S be the set of members named b of the object obtained by evaluating expression a (hereafter shortened to just "object a") that are accessible via the namespace rules applied in the lexical scope where a.b is evaluated. All three models pick some member s  S. Clearly, if the set S is empty, then the member lookup fails. In addition, the Spice and pure Static models may sometimes deliberately fail even when set S is not empty. Except for such deliberate failures, if the set S contains only one member s, all three models return that element s. If the set S contains multiple members, the three models will likely choose different members.

Another interesting (and useful) tidbit is that the Static and Dynamic models always agree on the interpretation of member lookup operations of the form this.b. All three models agree on on the interpretation of member lookup operations of the form this.b in the case where b is a member defined in the current class.

A note about overriding: When a subclass D overrides a member m of its superclass C, then the definition of the member m is conceptually replaced in all instances of D. However, the three models are only concerned with the topmost class in which member m is declared. All three models handle overriding the way one would expect of an object-oriented language. They differ in the cases where class C has a member named m, subclass D of C has a member with the same name m, but D’s m does not override C’s m because C’s m is not visible inside D (it’s not well known, but such non-overriding does and must happen in C++ and Java as well).

Static Model

In the Static model we look at the static type Ts of expression a. Let S1 be the subset of S whose class is either Ts or one of Ts’s ancestors. We pick the member in S1 with the most derived class.

The pure static model above is implemented by Java and C++. It would not work well in that form in ECMAScript because many, if not most, expressions have type Any. Because type Any has no members, users would have to cast expression a to a given type T before they could access members of type T. Because of this we must extend the static model to handle the case where the subset S1 is empty, or, in other words, the static lookup fails. (Rather than doing this, we could extend the static model in the case where the static type Ts is some special type, but then we would have to decide which types are special and which ones are not. Any is clearly special. What about Object? What about Array? It’s hard to draw the line consistently.)

In whichever cases way we extend the static model, we also have a choice of which member we choose. We could back off to the dynamic model, we could choose the most derived member in S, or perhaps we could choose some other approach.

Constraints:

Safety Good within the pure static model. Problems in the extended static model (a subclass could silently shadow a member) that could perhaps be addressed by warnings.
Abstraction Good.
Robustness   Very bad. Updating a function’s or global variable return type silently changes the meaning of all code that uses that function or global variable; in a large project such a change would be quite difficult. Difficult to correctly split expressions into subexpressions.
Namespace independence   Good.
Compatibility Bad within the pure static model (type casts needed everywhere). May be good in the extended static model, depending on the choice of how we extend it.
Other

This model may be difficult to compile well because the compiler may have difficulty in determining the intermediate types in compound expressions. Languages based on the static model have traditionally been compiled off-line, and such compilers tend to be difficult to write for on-line compilation without requiring the programmer to predeclare all of his data structures (if there are any forward-referenced ones, then the compiler doesn’t know whether they should have a type or not). A more dynamic execution model may actually help because it defers compilation until more information is known.

Spice Model

In the Spice model we think of each member m defined in a class C as though it were a function definition for a (possibly overloaded) function whose first argument has type C. Definitions in an inner lexical scope shadow definitions in outer scopes. The Spice model does not consider the static type Ts of expression a.

Let L be the innermost lexical scope enclosing the member lookup expression a.b such that some member named b is defined in L. Let Lb be the set of all members named b defined in lexical scope L, and let S1 = S  Lb (the intersection of S and Lb). If S1 is empty, we fail. If S1 contains exactly one member s, we use s. If S1 contains several members, we fail (this would only happen for import conflicts).

Constraints:

Safety Good.
Abstraction Good.
Robustness   Poor. Renaming an internal member may break code outside the class that defines that member even if that code does not access that member. Converting a member from private to one of the other two visibilities also can introduce conflicts in other, unrelated classes in the same package that just happen to have an unrelated member with the same name. Fortunately these conflicts usually (but not always) result in errors rather than silent changes to the meaning of the program, so one can often find them by exhaustively testing the program after making a change.
Namespace independence   Bad. Members with the same name in unrelated classes often conflict.
Compatibility Poor? Many existing programs rely on namespace independence and would have to be restructured.
Other

Most object-oriented programmers would be confused by a violation of namespace independence. Programming without this assumption requires a different point of view than most programmers are used to. (I am not talking about Lisp and Self programmers, who are familiar with that way of thinking.)

[There are numerous other variants of the Spice model as well.]

Dynamic Model

In the Dynamic model we pick the member s in S defined in the innermost lexical scope L enclosing the member lookup expression a.b. We fail if the innermost such lexical scope L contains more than one member in S (this would only happen for import conflicts).

Constraints:

Safety Good at the language level, but see "other" below.
Abstraction Good.
Robustness   Good. All of these changes are easy to do.
Namespace independence   Good.
Compatibility Good.
Other

Packages using the dynamic model may be vulnerable to hijacking (coerced into doing something other than what the author intended) by a determined intruder. It is possible for a compiler to detect such vulnerabilities and warn about them.

Namespaces

The various models make it possible to get into situations where either there is no way to access a visible member of an object or it is not safe to do so (see member hijacking). In these cases we’d like to be able to explicitly choose one of several potential members with the same name. The :: namespace syntax allows this. The left operand of :: is an expression that evaluates to a package or class; we may also allow special keywords such as public, internal, or private instead of an expression here, or omit the expression altogether. The right operand of :: is a name. The result is the name qualified by the namespace.

As we have seen, the name b in a member access expression a.b does not necessarily refer to a unique accessible member of object a. In a qualified member access expression a.n::b, the namespace n narrows the set of members considered, although it’s possible that the set may still contain more than one member, in which case the lookup model again disambiguates. Let S be the set of members named b of object a that are accessible. The following table shows how a.n::b subsets set S depending on n:

n   Subset
None Only the dynamic member named b, if any exists
A class C The fixed member of C named b, if it exists; if not, try C’s superclass instead, and so on up the chain
A package P   The subset of S containing all accessible members of P
private The fixed member named b of the current class
internal The subset of S containing all accessible members that have package (internal) visibility
public The subset of S containing all accessible members that have public visibility

The :: operator serves a different role from the . operator. The :: operator produces a qualified name, while the . operator produces a value. A qualified name can be used as the right operand of .; a value cannot. If a qualified name is used in a place where a value is expected, the qualified name is looked up using the lexical scoping rules to obtain the value (most likely a global variable).

Dynamic Members

All of the models above address only access to fixed properties of a class. ECMAScript also allows one to dynamically add properties to individual instances of a class. For simplicity we do not provide access control or versioning on these dynamic properties — all of them are public and open to everyone. Because of the safety criterion, a member lookup of a private or internal member must choose the private or internal member even if there is a dynamic member of the same name. To satisfy the robustness criterion, we should treat public members as similarly as possible to private or internal members, so we always give preference to a fixed property when there is a dynamic property of the same name.

To access a dynamic property that is shadowed by a fixed property, we can either prefix the member’s name with :: or use an indirect property access.

Indirect Member Access

How should we define the behavior of the expression a[b] (assuming a’s class is not a typed array or other class that overrides the default meaning of the [] operator)? There are a couple of possibilities:

  1. We could evaluate the expression b to some string "s" and treat a[b] as though it were a.s. This is essentially what ECMAScript 3 does. Unfortunately it’s hard to keep this behavior consistent with ECMAScript 3 programs’ expectations (they expect no more than one member with the same name, etc.), and this kind of indirection is also vulnerable to hijacking. It may be possible to solve the hijacking problem by devising restricted variants of the [] operator such as a.n::[b] that follow the rules given in the namespaces section above.
  2. We could evaluate the expression b to some string "s" and treat a[b] as though it were a.::s, thus limiting our selection to dynamic members. Dynamic members are well-behaved, but this kind of behavior would violate the compatibility criterion when ECMAScript 3 scripts try to reflect an ECMAScript 4 object using the [] operator.

In general it seems like it would be a bad idea to extend the syntax of the string "s" to allow :: operators inside the string. Such strings are too easily forged to play the role of pointers to members.

Member Hijacking

[explain security attacks]


ECMAScript 4 Netscape Proposal
Rationale
Versioning
previousupnext

Tuesday, October 9, 2001

Motivation

As a package evolves over time it often becomes necessary to change its exported interface. Most of these changes involve adding definitions (top-level or class members), although occasionally a definition may be deleted or renamed. In a monolithic environment where all ECMAScript source code comes preassembled from the same source, this is not a problem. On the other hand, if packages are dynamically linked from several sources then versioning problems are likely to arise.

One of the most common avoidable problems is collision of definitions. Unless we solve this problem, an author of a library will not be able to add even one definition in a future version of his library because that definition’s name could already be in use by some client or some other library that a client also links with. This problem occurs both in the global scope and in the scopes of classes from which clients are allowed to inherit.

Example

Here’s an example of how such a collision can arise. Suppose that a library provider creates a library called BitTracker that exports a class Data. This library becomes so successful that it is bundled with all web browsers produced by the BrowsersRUs company:

package BitTracker;

public class Data {
  public field author;
  public field contents;
  function save() {...}
};

function store(d) {
  ...
  storeOnFastDisk(d);
}

Now someone else writes a web page W that takes advantage of BitTracker. The class Picture derives from Data and adds, among other things, a method called size that returns the dimensions of the picture:

import BitTracker;

class Picture extends Data {
  public method size() {...}
  field palette;
};

function orientation(d) {
  if (d.size().h >= d.size().v)
    return "Landscape";
  else
    return "Portrait";
}

The author of the BitTracker library, who hasn’t seen W, decides in response to customer requests to add a method called size that returns the number of bytes of data in a Data object. He then releases the new and improved BitTracker library. BrowsersRUs includes this library with its latest NavigatorForInternetComputing 17.0 browser:

package BitTracker;

public class Data {
  public field author;
  public field contents;
  public method size() {...}
  function save() {...}
};

function store(d) {
  ...
  if (d.size() > limit)
    storeOnSlowDisk(d);
  else
    storeOnFastDisk(d);
}

An unsuspecting user U upgrades his old BrowsersRUs browser to the latest NavigatorForInternetComputing 17.0 browser and a week later is dismayed to find that page W doesn’t work anymore. U’s granddaughter Alyssa P. Hacker tries to explain to U that he’s experiencing a name conflict on the size methods, but U has no idea what she is talking about. U attempts to contact the author of W, but she has moved on to other pursuits and is on a self-discovery mission to sub-Saharan Africa. Now U is steaming at BrowsersRUs, which in turn is pointing its finger at the author of BitTracker.

Solutions

How could the author of BitTracker have avoided this problem? Simply choosing a name other than size wouldn’t work, because there could be some other page W2 that conflicts with the new name. There are several possible approaches:

  • Naming conventions. We could require each defined name to be prefixed by the full name of the party from which this definition originates. Unfortunately, this would get tedious and unnecessarily impact casual uses of the language. Furthermore, this approach is impractical for the names of methods because it is often desirable to share the same method name across several classes to attain polymorphism; this would not be possible if Netscape’s objects all used the com_netscape_length method while MIT’s objects used the edu_mit_length method.
  • Explicit imports. We could require each client package to import every external name it references. This works reasonably well for global names but becomes tedious for the names of class members, which would have to be imported separately for each class. Alternatives exist for bulk importing members of a class, but they are somewhat complicated and do not work for interfaces or multiple inheritance.
  • Versions. We could require package authors to mark the names they export with explicit versions. A package’s developer could introduce a new version of the package with additional names as long as those names were made invisible to prior versions.

The last approach appears to be the most desirable because it places the smallest burden on casual users of the language, who merely have to import the packages they use and supply the current version numbers in the import statements. A package author has to be careful not to disturb the set of visible prior-version definitions when releasing an updated package, but authors of dynamically linkable packages are assumed to be more sophisticated users of the language and could be supplied with tools to automatically check updated packages’ consistency.

Versioning in ECMAScript 4

ECMAScript 4 employs namespaces to provide safe versioning. A package can export several namespaces, each of which provides a different view of the package’s contents. Each namespace corresponds to a version of the package’s API.

Terminology

A version describes the API of a package. A release refers to the entirety of a package, including its code. One release can export many versions of its API. A package developer should make sure that multiple releases of a package that export version V export exactly the same set of definitions in version V.

Example

As an example, suppose that a developer wrote a sorting package Sorter with functions sort and merge that called bubble sort in the initial version. In the next release the developer adds a function called stablesort and includes it in version V2. In a subsequent release V3, the developer changes the sort algorithm to a quicksort that calls stablesort as a subroutine and adds the permute function. That last release of the package might look like:

package Sorter {
explicit namespace V2;
explicit namespace V3;
internal const V2and3 = V2 V3;

public var serialNumber;

public function sort(compare: Function, array: Array):Array {...}
public function merge(compare: Function, array1: Array, array2: Array):Array {...}
V2and3 function stablesort(compare: Function, array: Array):Array {...}
V3 function permute(array: Array):Array {...}
}

Suppose, further, that client packages C1 and C2 both import Sorter. There is only one instance of Sorter running — the latest release. By default, both C1 and C2 see only Sorter’s original API. However, suppose that C2 is aware that Sorter has been extended and would like to also use some of its newer functionality. To do this, C2 evaluates the magic incantation

use namespace(Sorter.V2);

after it imports Sorter. This enables C2 to also see the stablesort function. Note that, in this example, both clients see the same sort and merge functions and the same serialNumber variable (in particular, if C1 wrote to serialNumber, then C2 would see the updated value), but only C2 can see the stablesort function. Both clients get the quicksort release of sort. If client package C1 defined its own stablesort function, then that function would not conflict with Sorter’s stablesort; furthermore, Sorter’s sort would still refer to Sorter’s stablesort in its internal subroutine call.

Had only the first release of Sorter been available, client C2 would obtain an error because Sorter.V2 would be undefined. Client C1 could run normally, although the sort function it calls would use bubble sort instead of the quicksort.

The example above illustrates versioning as it applies to a package’s globals. The same techniques can be used to add members to existing classes, and there versioning is much more useful.


ECMAScript 4 Netscape Proposal
Rationale
Named Function Parameters
previousupnext

Wednesday, January 29, 2003

Named function parameters had originally been part of the ECMAScript 4 proposal but were deferred to a future version of the language in order to keep ECMAScript 4 small. If implemented, named function parameters might behave as described in this section.

Overview

The named function parameter extension allows some function parameters to be passed by name instead of by position. Parameter names are simple strings. A function with three positional (one of which is optional) and four named parameters might be declared as:

function f(a: Number, b: Boolean, c: Number = 5, named x: Integer = 7, named y = null, named z = 34, named t = undefined)

Such a function can then be invoked as follows:

f(2, true, y: 5);
f(2, true, 8, z: 32, x: 9);

Named parameters are always optional and must include default values. A function call may specify positional arguments followed by named arguments. Positional parameters can only match positional arguments. Named parameters can only match named arguments. The same parameter may not be both positional and named.

The following sections explain the changes to add named function parameters in more detail.

Lexer

A new non-reserved keyword named named is added. This keyword is a valid identifier:

Identifier 
   Identifier
|  get
|  set
|  exclude
|  include
|  named

Function Parameter Lists

A function may take zero or more positional parameters followed by either an optional rest parameter followed by zero or more named parameters or a named rest parameter:

Parameters 
   «empty»
NonemptyParameters 
RestAndNamedParameters 
NamedParameters 

Individual parameters have the forms:

ParameterCore  TypedIdentifierallowIn
Parameter 
|  const ParameterCore
ParameterInit 
   Parameter
NamedParameterCore  TypedIdentifierallowIn = AssignmentExpressionallowIn
NamedParameter 
   named NamedParameterCore
|  const named NamedParameterCore
|  named const NamedParameterCore
RestParameter 
   ...
|  ... Parameter
NamedRestParameter 
   ... named Identifier
|  ... const named Identifier
|  ... named const Identifier

If a Parameter is followed by a =, then that parameter is optional. If a function call does not provide an argument for an optional parameter, then that parameter is set to the value of its AssignmentExpression, implicitly coerced to the parameter’s type if necessary.

If a parameter is prefixed with named, then the parameter is matched by name rather than by position. Named parameters are always optional and must have initializers.

Attempting to define a function with two different parameters with the same name is an error.

A function with named parameters is never unchecked.

Rest Parameter

If the ... is present, the function accepts arguments not matched by any of the other listed parameters. If a parameter is given after the ..., then that parameter’s identifier is bound to an array of all remaining arguments. That identifier is declared as a local const using the parameter’s type, which defaults to Array. If the rest parameter is named, then the parameter’s type is always Array and cannot be specified explicitly.

The remaining positional arguments are stored as elements of the rest array with numeric indices starting from 0. If the rest parameter is not named, then there must be no remaining named arguments. Otherwise, extra named arguments are allowed and are stored as named properties of the rest array.

Call Processing

When a function is called, the following list indicates the order of evaluation of the various expressions in a FunctionDefinition. These steps are taken only after all of the argument names and values have been evaluated.

  1. If the function is unchecked, bind the arguments local variable to an array of all arguments and their names.
  2. Get the saved type t that was the result of evaluating the first parameter’s TypeExpression at the time the function was defined.
  3. If the first parameter is required and no positional argument has been supplied for it, then raise an error unless the function is unchecked, in which case let undefined be the first parameter’s value.
  4. If the first parameter is optional and there is a positional argument remaining, use the value of the positional argument. If there are no remaining positional arguments, then evaluate the first parameter’s AssignmentExpression and let it be the first parameter’s value.
  5. If the first parameter is named and there is a named argument with a matching argument name, then use the value of that named argument. Otherwise, evaluate the first parameter’s AssignmentExpression and let it be the first parameter’s value.
  6. Implicitly coerce the argument (or default) value to type t and bind the parameter’s Identifier to the result.
  7. Repeat steps 2-6 for each additional required, optional, and named parameter.
  8. If there is a RestParameter and one or more of the remaining arguments is named, raise an error.
  9. If there is a RestParameter with an Identifier, bind that Identifier to an array of the remaining positional arguments using indices starting from 0.
  10. If there is a NamedRestParameter, bind its Identifier to an array of the remaining positional arguments using indices starting from 0 as well as the remaining named arguments stored using named properties.
  11. If there is no RestParameter or NamedRestParameter and any arguments remain, raise an error unless the function is unchecked.
  12. Evaluate the body.
  13. Get the saved type r that was the result of evaluating the result TypeExpression at the time the function was defined.
  14. Implicitly coerce the result to type r and return it.

Function Calls

The a[args] and f(args) indexing and function call operators are extended to allow named arguments:

Brackets 
   [ ]
|  [ ListExpressionallowIn ]
Arguments 
ParenExpressions 
   ( )
NamedArgumentList 

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. Named arguments must follow positional arguments, but otherwise the order of named arguments is immaterial.


ECMAScript 4 Netscape Proposal
Rationale
Operators
previousupnext

Thursday, May 22, 2003

User-overridable operators had originally been part of the ECMAScript 4 proposal but were deferred to a future version of the language in order to keep ECMAScript 4 small. If implemented, operator overriding might behave as described in this section.

Overview

ECMAScript 4 provides a number of operators such as such as +, *, a[b], and -=. These operators are dispatched based on the types of their arguments. Some operators are dispatched only on one operand, in which case the dispatch behaves like property lookup. Other operators are dispatched on two operands simultaneously. Finally, some operators are syntactic sugars for combinations of existing operators.

There are predefined definitions for all ECMAScript operators. In addition, a class may provide additional definitions by overriding the existing behavior. Such overrides apply only when at least one of the dispatched operands is an instance of that class or its descendants. Operator overriding is useful, for example, to implement units without having to add units to the core of the ECMAScript 4 language.

This section presents the expansions of operators in ECMAScript source code, the built-in definitions of those operators, and the means of overriding them.

Invoking Operators

When an operator is invoked in an expression, the corresponding operator is invoked according to the table below. The operator invocations use pseudo-syntax to denote which operator is called; only the expression syntax on the left side of the table can be used to invoke operators.

Expression Operator Invocation
+x operator "+"(x)
-x operator "-"(x)
~x operator "~"(x)
++x (x = operator "++"(x))
x++ ( = xx = operator "++"(x), ), where is a temporary variable
--x (x = operator "--"(x))
x-- ( = xx = operator "--"(x), ), where is a temporary variable
x(a1, ...an) operator "()"(xa1, ...an)
new x operator "new"(x)
new x(a1, ...an) operator "new"(xa1, ...an)
x[a1, ...an] operator "[]"(xa1, ...an)
x[a1, ...an] = y operator "[]="(xya1, ...an)
delete x[a1, ...an]   operator "delete[]"(xa1, ...an)
x + y operator "+"(xy)
x - y operator "-"(xy)
x * y operator "*"(xy)
x / y operator "/"(xy)
x % y operator "%"(xy)
x << y operator "<<"(xy)
x >> y operator ">>"(xy)
x >>> y operator ">>>"(xy)
x < y operator "<"(xy)
x > y operator "<"(yx)
x <= y operator "<="(xy)
x >= y operator "<="(yx)
x in y operator "in"(xy)
x == y operator "=="(xy)
x != y !(operator "=="(xy))
x === y operator "==="(xy)
x !== y !(operator "==="(xy))
x & y operator "&"(xy)
x ^ y operator "^"(xy)
x | y operator "|"(xy)
x += y (x = operator "+"(xy))
x -= y (x = operator "-"(xy))
x *= y (x = operator "*"(xy))
x /= y (x = operator "/"(xy))
x %= y (x = operator "%"(xy))
x <<= y (x = operator "<<"(xy))
x >>= y (x = operator ">>"(xy))
x >>>= y (x = operator ">>>"(xy))
x &= y (x = operator "&"(xy))
x ^= y (x = operator "^"(xy))
x |= y (x = operator "|"(xy))

Operator Dispatch

Unary operators are single-dispatched just like regular methods. in is like a unary operator in that it can only be overridden by its right operand’s class, and it is single-dispatched by its right operand. Like in regular method dispatch, null is considered to be a member of only the types Null and Object.

Binary operator methods are double-dispatched based on the types of both operands. When one of the binary operators is invoked in an expression a  b, all applicable operator ""(xy) methods are examined to find the most specific one, which is then called. A definition of operator ""(x:Xy:Y) is applicable to a  b if the value of a is a member of X and the value of b is a member of Y, except that, like in regular method dispatch, null is considered to be a member of only the types Null and Object. A definition of operator ""(x:Xy:Y) is most specific if it is applicable and if every applicable definition of operator ""(x:X'y:Y') satisfies X X' and Y Y'. If there is no most specific applicable definition of or if there is more than one most specific applicable definition of , then an error occurs when the expression a  b is evaluated.

One of the operands of an operator expression inside a class C can be a super expression, in which case the set of applicable definitions of the operator is restricted to those defined to take a parameter of type T in that position, where T is any proper ancestor of C. This way a method overriding an operator can invoke its superclass’s definition of that operator.

Expression Grammar Changes

To accommodate super expressions, the grammar rules for expressions would be changed as described below.

Super Expressions

The SuperExpression grammar rule is split into two to make FullSuperExpression into a separate rule:

SuperExpression 
   super
FullSuperExpression  super ParenExpression

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 of one of the following operators:

  • The left operand of a . (property lookup), [] (indexing), () (call), new, or new() operator (for the () and new() operators the SuperExpression must be a FullSuperExpression)
  • The right operand of an in operator
  • The operand of a unary +, -, ~, ++, or -- operator
  • Either operand of a binary +, -, *, /, %, <<, >>, >>>, |, ^, &, <, >, <=, >=, ==, !=, ===, or !== operator
  • Either operand of a +=, -=, *=, /=, %=, <<=, >>=, >>>=, |=, ^=, or &= operator

super 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.

Postfix Expressions

These grammar rules are modified to permit super expressions as operands of () (call), new, new(), and postfix ++ and --. Unmodified rules are not shown below.

PostfixExpressionOrSuper 
FullNewExpression 
ShortNewExpression 
|  new SuperExpression

Unary Operators

These grammar rules are modified to permit super expressions as operands of unary +, -, and ~ as well as prefix ++ and -- operators.

UnaryExpression 
|  delete PostfixExpression
|  void UnaryExpression
|  typeof UnaryExpression
|  - NegatedMinLong
UnaryExpressionOrSuper 

Multiplicative Operators

These grammar rules are modified to permit super expressions as operands of binary *, /, and % operators.

MultiplicativeExpressionOrSuper 

Additive Operators

These grammar rules are modified to permit super expressions as operands of binary + and - operators.

AdditiveExpressionOrSuper 

Bitwise Shift Operators

These grammar rules are modified to permit super expressions as operands of the <<, >>, and >>> operators.

ShiftExpressionOrSuper 

Relational Operators

These grammar rules are modified to permit super expressions as operands of the <, >, <=, and >= operators and the right operand of an in operator.

RelationalExpressionOrSuper 
   RelationalExpression

Equality Operators

These grammar rules are modified to permit super expressions as operands of the ==, !=, ===, and !== operators.

EqualityExpression 
   RelationalExpression
|  EqualityExpressionOrSuper == RelationalExpressionOrSuper
|  EqualityExpressionOrSuper != RelationalExpressionOrSuper
|  EqualityExpressionOrSuper === RelationalExpressionOrSuper
|  EqualityExpressionOrSuper !== RelationalExpressionOrSuper
EqualityExpressionOrSuper 
   EqualityExpression

Binary Bitwise Operators

These grammar rules are modified to permit super expressions as operands of the &, ^, and | operators.

BitwiseAndExpression 
   EqualityExpression
|  BitwiseAndExpressionOrSuper & EqualityExpressionOrSuper
BitwiseXorExpression 
   BitwiseAndExpression
|  BitwiseXorExpressionOrSuper ^ BitwiseAndExpressionOrSuper
BitwiseOrExpression 
   BitwiseXorExpression
|  BitwiseOrExpressionOrSuper | BitwiseXorExpressionOrSuper
BitwiseAndExpressionOrSuper 
   BitwiseAndExpression
BitwiseXorExpressionOrSuper 
   BitwiseXorExpression
BitwiseOrExpressionOrSuper 
   BitwiseOrExpression

Assignment Operators

These grammar rules are modified to permit super expressions as operands of the +=, -=, *=, /=, %=, <<=, >>=, >>>=, |=, ^=, and &= operators.

AssignmentExpression 
   ConditionalExpression
|  PostfixExpression = AssignmentExpression

Built-In Operator Definitions

All operators have built-in definitions. These are summarized in the table below.

Name Signature Behavior
"()" (x:Object, ... args) Call x with the arguments args and return the result.
"new" (x:Object, ... args) Call x’s constructor slot with the arguments args and return the result.
"[]" (x:Object, n:Object) Return the value of x’s most derived public property named n.toString(). See property lookup.
"[]=" (x:Object, y:Object, n:Object) Set the value of x’s most derived public property named n.toString() to y.
(x:Array, y:Object, n:Object) Set the value of x’s most derived public property named n.toString() to y. Update the length property as in ECMAScript 3.
"delete[]" (x:Object, n:Object) Try to delete x’s most derived public property named n.toString().
"~" (x:Object) Return the bitwise complement of ToInt32(operator "+"(x)).
"++" (x:Object) Return operator "+"(operator "+"(x), 1).
"--" (x:Object) Return operator "-"(operator "+"(x), 1).
"+" (x:Object) Return ToNumber(x).
(x:Object, y:Object) Let x' = ToPrimitive(x) and y' = ToPrimitive(y). If at least one of x' and y' is a string, then return operator "+"(x'y'). Otherwise return ToNumber(x') + ToNumber(y').
(x:String, y:Object) Return x concatenated with y.toString().
(x:Object, y:String) Return x.toString() concatenated with y.
(x:String, y:String) Return x concatenated with y.
(x:Number, y:Number) Return x + y.
"-" (x:Object) Return -ToNumber(x).
(x:Object, y:Object) Return ToNumber(x) – ToNumber(y).
"*" (x:Object, y:Object) Return ToNumber(x ToNumber(y).
"/" (x:Object, y:Object) Return ToNumber(x) / ToNumber(y).
"%" (x:Object, y:Object) Return ToNumber(x) % ToNumber(y).
"<<" (x:Object, y:Object) Return ToNumber(x) << ToNumber(y).
">>" (x:Object, y:Object) Return ToNumber(x) >> ToNumber(y).
">>>" (x:Object, y:Object) Return ToNumber(x) >>> ToNumber(y).
"<" (x:Object, y:Object) Same as in ECMAScript 3.
">=" (x:Object, y:Object) Same as in ECMAScript 3.
"in" (x:Object, y:Object) Same as in ECMAScript 3.
"==" (x:Object, y:Object) Same as in ECMAScript 3.
"===" (x:Object, y:Object) Same as in ECMAScript 3.
"&" (x:Object, y:Object) Return ToNumber(x) & ToNumber(y).
"^" (x:Object, y:Object) Return ToNumber(x) ^ ToNumber(y).
"|" (x:Object, y:Object) Return ToNumber(x) | ToNumber(y).

Operator Overriding

An operator can be overridden only inside a class C. That class should define a function with the operator attribute (operator is an additional member modifier attribute). The function’s name must be one of the strings in the table below. The name must be a string rather than an identifier; the FunctionName grammar rule is extended to allow strings:

FunctionName 
|  get [no line break] Identifier
|  set [no line break] Identifier
|  String

A FunctionName can be a String only for operator overrides.

The table also defines the acceptable signatures that the operator can have. At least one parameter must be specified as taking a value of type C (a type that is a subclass of C is not acceptable). If the operator is ===, then both parameters must take values of type C. A class can define several different signatures of the same binary (but not unary) operator, differing in the choice of parameter types X or Y, which may be omitted if they are Object. The parameters denoted as taking types C, X, or Y in the table below cannot be optional or named parameters. The return type of an operator override can be any type.

Name Signatures
"~"
"++"
"--"
(x:C)
"+"
"-"
(x:C)
(x:Cy:Y)
(x:Xy:C)
(x:Cy:C)
"*"
"/"
"%"
"<<"
">>"
">>>"
"<"
"<="
"=="
"&"
"^"
"|"
(x:Cy:Y)
(x:Xy:C)
(x:Cy:C)
"===" (x:Cy:C)
"in" (x:Xy:C)
"()"
"new"
"[]"
"delete[]"
(x:Ca1, ...an)
"[]=" (x:Cy:Ya1, ...an)

The operator methods should return the result of the operator. For the ++ and -- operators, the result is the incremented or decremented value. The (), new, [], delete[], and []= operators take additional argument lists. If desired, these argument lists can include optional, named, or rest arguments. The []= operator must take one more initial required parameter, which is the value stored in the element.

The >, >=, !=, and !== operators cannot be overridden directly because they are syntactic sugars for invocations of the <, <=, ==, and === operators; override the latter operators instead. The !, ||, ^^, &&, and ?: operators cannot be overridden directly, but they are affected by any redefinition of toBoolean. The reason for not overriding ||, &&, and ?: is that they do not always evaluate all of their arguments, and programmers often take advantage of this short-circuiting behavior. ! is not overridable in order to preserve useful identities such as:

  • if (!xA else B if (xB else A
  • !(x && y) !x || !y
  • x != y !(x == y)

Definitions of operators cannot specify a namespace. No access restrictions can be imposed on operators.

Example

The following class illustrates overriding a few operators.

class Complex {
  var x:Number, y:Number;

  operator function "+"(a:Complex):Complex {
    return a;
  }

  operator function "-"(a:Complex):Complex {
    return new Complex(x: -a.x, y: -a.y);
  }

  operator function "+"(a:Complex, b:Complex):Complex {
    return new Complex(x: a.x+b.x, y: a.y+b.y);
  }

  operator function "+"(a:Complex, b:Number):Complex {
    return new Complex(x: a.x+b, y: a.y);
  }

  operator function "+"(a:Number, b:Complex):Complex {
    return new Complex(x: a+b.x, y: b.y);
  }

  function toBoolean():Boolean {
    return this.x != 0 || this.y != 0;
  }
}

toBoolean Method

Just like toString, every object has a toBoolean method that can be overridden. This method is called by statements such as if, while, do while, and for and operators such as !, ||, ^^, &&, and ? :. This method should return either true or false.

For-In Operator

An object x’s class can override the meaning of for-in loops such as for (v in x) by overriding the following three methods which are located in the system-defined namespace Iterator:

Method Description
x.Iterator::forIn() If the iteration is empty, return null. Otherwise, return an object o with public properties named value and state (o may also have other properties). The value of o.value is the first element returned by the iteration. The value of o.state can have any type and will be passed exactly once to either Iterator::next or Iterator::done. The value of o is not guaranteed to be valid after o.state is passed to Iterator::next or Iterator::done.
x.Iterator::next(i) i was returned by a previous call to Iterator::forIn or Iterator::next. If the iteration is done, return null. Otherwise, return an object o with public properties named value and state (o may also have other properties). The value of o.value is the next element returned by the iteration. The value of o.state can have any type and will be passed exactly once to either Iterator::next or Iterator::done. The value of o is not guaranteed to be valid after o.state is passed to Iterator::next or Iterator::done.
x.Iterator::done(i) i was returned by a previous call to Iterator::forIn or Iterator::next. This call alerts x that the iteration is exiting prematurely and it can do whatever cleanup it needs on i. Every i returned by Iterator::forIn or Iterator::next is guaranteed to be subsequently passed exactly once either to Iterator::next or Iterator::done. Iterator::done should return undefined.

Rationale

A few other approaches had been considered for overriding the for-in operator. The three-method approach above was chosen for the following reasons:

  • Why use an iterator object instead of a function and a callback (i.e. have Iterator::forIn take a parameter f and have it call f for each element in the collection)? The reason is that the iterator model permits future extensions such as iterating through two collections concurrently. This cannot be done using callbacks.
  • Why return an object from Iterator::forIn and Iterator::next instead of having Iterator::forIn and Iterator::next return just an iterator and having a separate Iterator::get method that, given an iterator, returns an element? The rationale here is that the separate Iterator::get method would introduce unavoidable concurrency hazards. Since null (or any other value) can be a member of a collection, we can’t use the return value from Iterator::get to detect the end of iteration; instead, we’d have to use a null return value from Iterator::forIn or Iterator::next to indicate the end of iteration. In such a case, however, it would be possible for another thread to delete the last element between the call to Iterator::forIn or Iterator::next and the call to Iterator::get, and Iterator::get would then be stuck. We could use an exception to indicate the end of iteration, but this would put exception handling on the fast path of program execution.
  • Why use an object with two named slots rather than a two-element read-only array for the return value? Named slots can have heterogeneous types, while the types of array elements must be the same.
  • Why define Iterator::done? Some objects may erect temporary scaffolding to support iteration. This method informs them that they can take down such scaffolding. Since ECMAScript currently lacks finalization, there is no other way for the iterated object to know that the iteration aborted early — the start of another iteration cannot be used as an indication that the previous iteration completed because several iterations can be happening simultaneously on the same object.

Class Reflection Operator

Earlier drafts of this proposal had a .class operator that returned an instance’s class. This operator was defined using the grammar production:

PropertyOperator 
|  . class

The operator was removed because obtaining an instance’s class breaks abstraction. The client of a factory method m could determine whether m was returning instances of some stated class C or subclasses of C, and m could not evolve to alter its implementation decision of what precise instances to return without potentially breaking clients.


ECMAScript 4 Netscape Proposal
Rationale
Units
previousupnext

Friday, September 20, 2002

Units derived from the Spice proposals had originally been part of the ECMAScript 4 proposal but were deferred to a future version of the language in order to keep ECMAScript 4 small. If implemented, units might behave as described in this section.

Lexing Units

A unit expression usually consists of a number followed by a unit name with no intervening white space, as in 7cm. The unit name can be quoted (for example, 7 "cm"), in which case white space is permitted between the number and the unit and the unit name can instead be a unit pattern (for example, 7 "Kg*cm^2/s").

When a numeric literal is immediately followed by an identifier, the lexer converts the identifier to a string literal. The parser then treats the number and string as a unit expression. The identifier cannot start with an underscore, but there are no reserved word restrictions on the identifier; any identifier that begins with a letter will work, even if it is a reserved word.

For example, 3in is converted to 3 "in". 5xena is converted to 5 "xena". On the other hand, 0xena is converted to 0xe "na". It is unwise to define unit names that begin with the letters e or E either alone or followed by a decimal digit, or x or X followed by a hexadecimal digit because of potential ambiguities with exponential or hexadecimal notation.

Unit Expressions

PrimaryExpression 
   null
|  true
|  false
|  public
|  Number
|  String
|  this
|  RegularExpression
UnitExpression 
|  Number [no line break] String
|  UnitExpression [no line break] String

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 pattern 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 idi in the system unit namespace. If ei is 1, let Fi = Vi; otherwise, let Fi = Vi.public::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.public::pow(2)*unit::s.public::pow(–2)*unit::q.public::pow(–1), where unit is the system unit namespace (the distinction between unit and the name unit is only relevant for perverse code that has a local definition named unit; the presence of such a local definition doesn’t affect unit lookup).

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.public::pow(2)*unit::s.public::pow(–2)*unit::q.public::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")).

Defining Units

Units are defined by placing them in the unit namespace, which is predefined by the system. Unit expressions are implicitly qualified using the unit namespace. The unit namespace is not used by default, so a program needs to explicitly qualify identifiers with unit:: to access existing unit definitions..

The easiest way to define new units is to scale, multiply, or divide existing ones. 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.

Unit Class Extension

If class extensions were also added to the language, the class extension mechanism could be used instead of a unit namespace to define units. This way units could be designated as internal, private, etc.

The unit attribute would be defined as though

const unit = extend(Unit);

were evaluated at the top level. Unit would be the class that holds the definitions of unit names.


ECMAScript 4 Netscape Proposal
Rationale
Unit Patterns
previousupnext

Monday, June 30, 2003

This LALR(1) grammar describes the syntax of quoted unit patterns. The semantics describe the actions the lexer takes in order to interpret a quoted unit pattern. The input to this grammar and semantics is the unit string literal’s contents after the string literal has been processed. See also the description of the semantic notation.

This document is also available as a Word RTF file.

The start nonterminal is UnitPattern.

White Space

Syntax

  {wsoptwsreq}
WhiteSpaceCharacter 
   «TAB» | «VT» | «FF» | «SP» | «u00A0»
|  «u2000» | «u2001» | «u2002» | «u2003» | «u2004» | «u2005» | «u2006» | «u2007»
|  «u2008» | «u2009» | «u200A» | «u200B»
|  «u3000»
LineTerminator  «LF» | «CR» | «u0085» | «u2028» | «u2029»
RequiredWhiteSpace 
WhiteSpacewsopt 
|  «empty»
WhiteSpacewsreq  RequiredWhiteSpace

Unit Patterns

Syntax

UnitPattern  WhiteSpacewsopt UnitQuotient
UnitQuotient 
   UnitProductwsopt
|  UnitProductwsopt / WhiteSpacewsopt UnitProductwsopt
UnitProduct 
   UnitFactor
|  UnitProductwsopt * WhiteSpacewsopt UnitFactor
|  UnitProductwsreq UnitFactor
UnitFactor 
   1 WhiteSpace
|  1 WhiteSpacewsopt ^ WhiteSpacewsopt SignedInteger WhiteSpace
|  Identifier WhiteSpace
|  Identifier WhiteSpacewsopt ^ WhiteSpacewsopt SignedInteger WhiteSpace

Semantics

tuple UnitFactor
identifierString,
exponentInteger
end tuple;
UnitList = UnitFactor[];
proc unitReciprocal(valueUnitList): UnitList
return [UnitFactoridentifierf.identifierexponent: –f.exponent | f  value]
end proc;
Value[UnitPattern  WhiteSpacewsopt UnitQuotient]: UnitList = Value[UnitQuotient];
Value[UnitQuotient  UnitProductwsopt] = Value[UnitProductwsopt];
Value[UnitQuotient  UnitProductwsopt1 / WhiteSpacewsopt UnitProductwsopt2] = Value[UnitProductwsopt1 unitReciprocal(Value[UnitProductwsopt2]);
Value[UnitProduct]: UnitList;
Value[UnitProduct  UnitFactor] = Value[UnitFactor];
Value[UnitProduct0  UnitProductwsopt1 * WhiteSpacewsopt UnitFactor] = Value[UnitProductwsopt1 Value[UnitFactor];
Value[UnitProduct0  UnitProductwsreq1 UnitFactor] = Value[UnitProductwsreq1 Value[UnitFactor];
Value[UnitFactor]: UnitList;
Value[UnitFactor  1 WhiteSpace] = [];
Value[UnitFactor  1 WhiteSpacewsopt ^ WhiteSpacewsopt SignedInteger WhiteSpace] = [];
Value[UnitFactor  Identifier WhiteSpace] = [UnitFactoridentifierName[Identifier], exponent: 1];
Value[UnitFactor  Identifier WhiteSpacewsopt ^ WhiteSpacewsopt SignedInteger WhiteSpace] = [UnitFactoridentifierName[Identifier], exponentIntegerValue[SignedInteger]];

Signed Integers

Syntax

SignedInteger 
DecimalDigits 
ASCIIDigit  0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9

Semantics

IntegerValue[SignedInteger]: Integer;
IntegerValue[SignedInteger  DecimalDigits] = IntegerValue[DecimalDigits];
IntegerValue[SignedInteger  + DecimalDigits] = IntegerValue[DecimalDigits];
IntegerValue[SignedInteger  - DecimalDigits] = –IntegerValue[DecimalDigits];
IntegerValue[DecimalDigits]: Integer;
IntegerValue[DecimalDigits  ASCIIDigit] = DecimalValue[ASCIIDigit];
IntegerValue[DecimalDigits0  DecimalDigits1 ASCIIDigit] = 10IntegerValue[DecimalDigits1] + DecimalValue[ASCIIDigit];
DecimalValue[ASCIIDigit]: Integer = digitValue(ASCIIDigit);

Identifiers

Syntax

Identifier 
InitialIdentifierCharacter  UnicodeInitialAlphabetic | $ | _
ContinuingIdentifierCharacter  UnicodeAlphanumeric | $ | _
UnicodeInitialAlphabetic  Any Unicode initial alphabetic character (includes ASCII A-Z and a-z)
UnicodeAlphanumeric  Any Unicode alphabetic or decimal digit character (includes ASCII 0-9, A-Z, and a-z)

Semantics

Name[Identifier0  Identifier1 ContinuingIdentifierCharacter] = Name[Identifier1 [ContinuingIdentifierCharacter];

ECMAScript 4 Netscape Proposal
Rationale
Syntax
previousupnext

Tuesday, November 19, 2002

This section presents a number of syntactic alternatives that were considered while developing this proposal.

Semicolon Insertion

Definitions

The term semicolon insertion informally refers to the ability to write programs while omitting semicolons between statements. In both ECMAScript 3 and ECMAScript 4 there are two kinds of semicolon insertion:

Grammatical Semicolon Insertion
Semicolons before a closing } and the end of the program are optional in both ECMAScript 3 and 2.0. In addition, the ECMAScript 4 parser allows semicolons to be omitted before the else of an if-else statement and before the while of a do-while statement.
Line-Break Semicolon Insertion
If the first through the nth tokens of an ECMAScript program form are grammatically valid but the first through the n+1st tokens are not and there is a line break between the nth tokens and the n+1st tokens, then the parser tries to parse the program again after inserting a VirtualSemicolon token between the nth and the n+1st tokens.

Grammatical semicolon insertion is implemented directly by the syntactic grammar’s productions, which simply do not require a semicolon in the aforementioned cases. Line breaks in the source code are not relevant to grammatical semicolon insertion.

Line-break semicolon insertion cannot be easily implemented in the syntactic grammar. This kind of semicolon insertion turns a syntactically incorrect program into a correct program and relies on line breaks in the source code.

Discussion

Grammatical semicolon insertion is harmless. On the other hand, line-break semicolon insertion suffers from the following problems:

  1. Line breaks are relevant in the program’s source code
  2. The consequences of this kind of semicolon insertion appear inconsistent to programmers
  3. Existing program behavior can change unexpectedly when new syntax is introduced

The first problem presents difficulty for some preprocessors such as the one for XML attributes which turn line breaks into spaces. The second and third ones are more serious. Programmers are confused when they discover that the program

a = b + c
(d + e).print()

doesn’t do what they expect:

a = b + c;
(d + e).print();

Instead, that program is parsed as:

a = b + c(d + e).print();

The third problem is the most serious. New features are added to the language turn illegal syntax into legal syntax. If an existing program relies on the illegal syntax to trigger line-break semicolon insertion, then the program will silently change behavior once the feature is added. For example, the juxtaposition of a numeric literal followed by a string literal (such as 4 "in") is illegal in ECMAScript 3. ECMAScript 4 makes this legal syntax for expressions with units. This syntax extension has the unfortunate consequence of silently changing the meaning of the following ECMAScript 3 program:

a = b + 4
"in".print()

from:

a = b + 4;
"in".print();

to:

a = b + 4"in".print();

ECMAScript 4 gets around this incompatibility by adding a [no line break] restriction in the grammar that requires the numeric and string literals to be on the same line. Unfortunately, this compatibility is a double-edged sword. Due to ECMAScript 3 compatibility, ECMAScript 4 has to have a large number of these [no line break] restrictions. It is hard to remember all of them, and forgetting one of them often silently causes an ECMAScript 4 program to be reinterpreted. Some programmers will be dismayed to find that:

internal
  function f(x) {return x*x}

turns into:

internal;
function f(x) {return x*x}

(where internal; is an expression statement) instead of:

internal function f(x) {return x*x}

An earlier version of ECMAScript 4 disallowed line-break semicolon insertion. The current version allows it but only in non-strict mode. Strict mode removes all [no line break] restrictions, simplifying the language again. As a side effect, it is possible to write a program that does different things in strict and non-strict modes (the last example above is one such program), but this is the price to pay to achieve simplicity.

Regular Expression Literals

ECMAScript 4 retains compatibility with ECMAScript 3 by adopting the same rules for detecting regular expression literals. This complicates the design of programs such as syntax-directed text editors and machine scanners because it makes it impossible to find all of the tokens in an ECMAScript program without parsing the program.

Making ECMAScript 4’s lexical grammar independent of its syntactic grammar significantly would have allowed tools to easily process an ECMAScript program and escape all instances of, say, </ to properly embed an ECMAScript 4 or later program in an HTML page. The full parser changes for each version of ECMAScript. To illustrate the difficulties, compare such ECMAScript 3 gems as:

for (var x = a in foo && "</x>" || mot ? z:/x:3;x<5;y</g/i) {xyz(x++);}
for (var x = a in foo && "</x>" || mot ? z/x:3;x<5;y</g/i) {xyz(x++);}

Alternate Regular Expression Syntax

One idea explored early in the design of ECMAScript 4 was providing an alternate, unambiguous syntax for regular expressions and encouraging the use of the new syntax. A RegularExpression could have been specified unambiguously using « and » as its opening and closing delimiters instead of / and /. For example, «3*» would be a regular expression that matches zero or more 3’s. Such a regular expression could be empty: «» is a regular expression that matches only the empty string, while // starts a comment. To write such a regular expression using the slash syntax one needs to write /(?:)/.

Syntactic Resynchronization

Syntactic resynchronization occurs when the lexer needs to find the end of a block (the matching }) in order to skip a portion of a program written in a future version of ECMAScript. Ordinarily this would not be a problem, but regular expressions complicate matters because they make lexing dependent on parsing. The rules for recognizing regular expression literals must be changed for those portions of the program. The rule below might work, or a simplified parse might be performed on the input to determine the locations of regular expressions. This is an area that needs further work.

During syntax resynchronization ECMAScript 4 determines whether a / starts a regular expression or is a division (or /=) operator solely based on the previous token:

/ interpretation Previous token
/ or /=   Identifier   Number   RegularExpression   String
)   ++   --   ]   }
class   false   null   private   protected   public   super   this   true
get   set
Any other punctuation
RegularExpression   !   !=   !==   #   %   %=   &   &&   &&=   &=   (   *   *=   +   +=   ,   -   -=   ->   .   ..   ...   /   /=   :   ::   ;   <   <<   <<=   <=   =   ==   ===   >   >=   >>   >>=   >>>   >>>=   ?   @   [   ^   ^=   ^^   ^^=   {   |   |=   ||   ||=   ~
abstract   break   case   catch   const   continue   debugger   default   delete   do   else   enum   export   extends   final   finally   for   function   goto   if   implements   import   in   instanceof   interface   is   namespace   native   new   package  return   static   switch   synchronized   throw   throws   transient   try   typeof   use   var   volatile   while   with

Regardless of the previous token, // is interpreted as the beginning of a comment.

The only controversial choices are ) and }. A / after either a ) or } token can be either a division symbol (if the ) or } closes a subexpression or an object literal) or a regular expression token (if the ) or } closes a preceding statement or an if, while, or for expression). Having / be interpreted as a RegularExpression in expressions such as (x+y)/2 would be problematic, so it is interpreted as a division operator after ) or }. If one wants to place a regular expression literal at the very beginning of an expression statement, it’s best to put the regular expression in parentheses. Fortunately, this is not common since one usually assigns the result of the regular expression operation to a variable.

Type Declarations

The current ECMAScript 4 proposal uses Pascal-style colons to introduce types in declarations. For example:

var x:Integer = 7;
function square(a:Number):Number {return a*a}

This is due to a consensus decision of the ECMA working group, with Waldemar the only dissenter. There are a couple of alternative syntaxes:

C-Style

We could allow modified C-style type declarations as long as a function’s return type is listed after its parameters:

var Integer x = 7;
var Integer y = 8, Integer z = 9;  // Declares two Integer variables
function square(Number a) Number {return a*a}

A function’s return type cannot be listed before the parameters because this would make the grammar ambiguous.

In fact, an implementation could unambiguously admit both the Pascal-style and the modified C-style declarations by replacing the TypedIdentifier and Result grammar rules with the ones listed below. The resulting grammar is still LALR(1).

TypedIdentifier 
|  Identifier : TypeExpression
|  TypeExpression Identifier
Result 
   «empty»
|  : TypeExpressionallowIn
|  [lookahead{{}] TypeExpressionallowIn

Advantages of using the modified C-style syntax include:

  • On the Pascal/Modula/Ada vs. C/C++/Java syntax debate, ECMAScript tends to use syntax more similar to Java.
  • We already use the colon syntax for statement labels and object literal elements (for example {a:17, b:33}). The latter would present a conundrum if we ever wanted to declare field types in an object literal. Some programmers have been using these as a convenient facility for passing named arguments to functions.

Attribute-Style

Since attributes are simple expressions, we could allow attributes that evaluate to types. For var and const declarations, these attributes would specify the type of the declared variables. For function declarations, these attributes would specify the function’s return type. For stylistic consistency, types of arguments would also be listed before their identifiers.

Integer var x = 7;
Integer var y = 8, z = 9;        // Declares two Integer variables
Number function square(Number a) {return a*a}

This style is simple and reads fairly naturally.

Again, an implementation could unambiguously admit both the Pascal-style and the attribute-style declarations, with the resulting grammar still being LALR(1). However, it’s better if the language made a choice rather than propagating the confusion of having two or three styles; this flexibility could be used for compatibility with existing programs.

Type Expressions

ECMAScript 4 uses the same syntax for type expressions as for value expressions for the following reasons:

  • Creating two different syntaxes for two kinds of expressions would add to the complexity of the language.
  • ECMAScript is a dynamic language and it is useful to manipulate types as though they were first-class values.
  • It’s difficult to unambiguously distinguish type expressions from value expressions. In the expression (expr1)(expr2), is expr1 a type or a value expression? If the two have the same syntax, it doesn’t matter.

Function Declarations

Getters and Setters

By consensus in the ECMA TC39 modularity subcommittee, we decided to use the syntax function get id (...) rather than getter function id (...) for defining a getter and function set id (...) rather than setter function id (...) for defining a setter. The latter would have simplified the FunctionName rule to:

FunctionName  Identifier

while creating two additional attributes, getter and setter. The decision was based on aesthetics; neither syntax is more difficult to implement than the other.

Language Directives

An alternative to pragmas that was considered early was to report syntax errors at the time the relevant statement was executed rather than at the time it was parsed. This way a single program could include parts written in a future version of ECMAScript without getting an error unless it tries to execute those portions on a system that does not understand that version of ECMAScript. If a program part that contains an error is never executed, the error never breaks the script. For example, the following function finishes successfully if whizBangFeature is false:

function move(x:Integer, y:Integer, d:Integer) {
  x += 10;
  y += 3;
  if (whizBangFeature) {
    simulate{@x and #y} along path
  } else {
    x += d; y += d;
  }
  return [x,y];
}

The code simulate{@x and #y} along path is a syntax error, but this error does not break the script unless the script attempts to execute that piece of code.

One problem with this approach is that it frustrates debugging; a script author benefits from knowing about syntax errors at compile time rather than at run time.


ECMAScript 4 Netscape Proposal
Rationale
Miscellaneous
previousupnext

Wednesday, June 4, 2003

This section presents a number of miscellaneous alternatives that were considered while developing this proposal.

Types

Object

The root of the type hierarchy was chosen to be the existing ECMAScript 3 type Object. Primitive numbers, strings, and booleans were made into instances of subclasses of Object and the existing wrapper classes eliminated, thereby eliminating the confusing distinction between boolean primitives and objects, etc. in ECMAScript 3.

The alternative was to define a new root type, any, that is an ancestor of Object as well as primitive numbers, strings, and booleans, which could have their own types number, string, and boolean respectively. In this scenario the ECMAScript 3 classes Number, String, and Boolean would still be subclasses of Object, but the primitive values could not be members of these classes — for example, false would be a member of the class boolean but not Boolean. The object obtained by boxing false would be a member of the class Boolean but not boolean and would evaluate to true (!) if used in an if statement.

The any alternative was more compatible with ECMAScript 3, but the added complexity and confusion was not deemed worthwhile for the extra compatibility. Instead, ECMAScript 4 simplifies and regularizes the behavior of ECMAScript 3 in this area.

Never and Void

The types Never and Void are different and serve different purposes. When used as a return type, Never describes the inability to return from a function, while Void states that the function returns, but the value returned is not useful (it’s always undefined).

The following example illustrates the use of Never and Void:

// This function returns no useful value.
function display(message:String):Void {
  document.write("<\_P>" + message + "<\/P>\n");
}

// This function cannot return.
function abort(message:String):Never {
  display(message);
  throw AbortException;
}

function chickenCount(myChickens:Array[Chicken]):Integer {
  if (notHatched(myChickens))
    abort("Can’t count the chickens yet");
  else
    return myChickens.length;
}

Note that if the function abort had no explicit return type or any return type other than Never, then the compiler would likely issue a warning inside the function chickenCount because it contains a code path (the false case of its if) that appears to fall out of the function without returning a value, while chickenCount is declared to return an Integer. The Never return type on abort tells the compiler that there is no such code path inside chickenCount.

It might be a good idea for a compiler to issue a warning for a function that is declared as returning type Never for which the compiler can’t verify that the function can’t return.

Typed Arrays

The proposal originally had additional array types which were dropped for simplicity. The following are some candidates for a future revision of the language:

Type Set of Values
Array Same as Array[Object]
Array[t] null as well as all arrays (dense or sparse) capable of holding elements of type t
List[t] null as well as all resizeable, dense arrays capable of holding elements of type t
t[] null as well as all nonresizeable, dense arrays capable of holding elements of type t
ConstArray Same as ConstArray[Object]
ConstArray[t] null as well as all constant arrays capable of holding elements of type t

A sparse array may have holes. A dense array always has contiguous elements indexed starting from zero. Resizeable arrays may be resized by writing to the length property.

When A is one of the type expressions Array[t], List[t], t[], ConstArray, or ConstArray[t], the following three operations can be used to create an array of type A or coerce to one:

  • new A(elt1elt2...) creates an instance of the corresponding array type with the given elements; this is true even if there is only one element given.
  • new A(length: n) creates an instance of the corresponding array type with a length of n. The initial elements are holes if the array is sparse or the default value for A’s element type if the array is dense. Note that this form requires the named function parameters extension.
  • A(B) creates an instance of the corresponding array type. The new instance’s elements are copied from B, which should be any kind of an array. It’s unresolved what should happen if A is a dense array and B has holes.

Instances of non-ConstArray array types are equal only when they are the same object. Instances of ConstArray array types are equal when they have the same contents and the same element type t.

Type Expressions

We could define other type operators such as the ones in the table below. s and t are type expressions.

Type   Values Implicit coercion of value v
t[] null as well as nonresizable arrays of values of type t undefined null
const t Makes type t, which must be an array type, into a read-only array type None
- t Any value belonging to type t except null
~ t undefined or any value belonging to type t undefined undefined; any other implicit coercions already defined for t
+t null or any value belonging to type t null null; undefined null (if undefined is not a member of t); any other implicit coercions already defined for t
s + t All values belonging to either type s or type t or both If vs+t, then use v; otherwise, if v as s is defined then use v as s; otherwise, if v as t is defined then use v as t.
s * t All values simultaneously belonging to both type s and type t If v as s as t is defined and is a member of s*t, then use v as s as t.
s / t All values belonging to type s but not type t If v as s is defined and is a member of s/t, then use v as s.

The [] suffix operator could be used to make array types. Unlike Array, these arrays would be dense, indexable only by integers, nonresizable, and non-subclassable — t[] has the class modifier final.

A new unary operator, const, could be added. This operator would take a PostfixExpressionOrSuper x as an operand. If x is an instance of a non-ConstArray array, const x would return a ConstArray[T] copy of x, where T is the most specific type such that every element of x is a member of T. If x is an array type, const x would be the corresponding ConstArray type.

  • t[] is the type of writable arrays of t.
  • const t[] is the type of constant arrays of t.
  • t[][] is the type of writable arrays of writable arrays of t.
  • const t[][] is the type of constant arrays of writable arrays of tconst binds looser than [] so it’s the same as const (t[])[].
  • (const t[])[] is the type of writable arrays of constant arrays of t.

Multiple Constructors

Earlier ECMAScript 4 proposals allowed a class to have multiple constructors with different names, callable using the same syntax as calling static functions. These were dropped to keep the language simple. Since now each class can have at most one constructor, the this(args) form for calling another constructor from the same class was also dropped. Constructors had been defined as described below.

A constructor is a function that creates a new instance of a class C. Constructors are defined using the attribute constructor. A constructor is usually given the same name as its class, in which case the constructor attribute is optional and the constructor is known as a default constructor and can be called as new C. If a constructor has a different name, it is invoked as though it were a static function of the class:

class C {
  var a:String;

  constructor function C(p:String) {this.a = "New "+p}      // The attribute constructor is optional here
  constructor function make(p:String) {this.a = "Make "+p}
  static function obtain(p:String):C {return new C(p)}
}

var c:C = new C("one");
var d:C = C.C("two");
var e:C = C.make("three");
var f:C = C.obtain("four");

c.a;     // Returns "New one"
d.a;     // Returns "New two"
e.a;     // Returns "Make three"
f.a;     // Returns "New four"

A constructor can refer to its class’s instance variables via this. If a class C inherits from class B, then when B’s constructor is called while creating an instance of C, B’s constructor will be able to call virtual methods of class C on the partially constructed instance. Likewise, B’s constructor could store this into a global variable v and some other function could call a method of C on the partially constructed object v. Class C’s methods can be assured that they are only called on fully initialized instances of C only if neither C nor any of its ancestors contains a constructor that exhibits either of the behaviors above.

A constructor should not return a value with a return statement; the newly created object is returned automatically. A constructor’s return type must be omitted.

A constructor function always returns a new instance. On the other hand, a static function can return an existing instance of its class. It is possible to define a static function with the same name as its class C; such a function looks like a constructor to the outside (it can be called as new C) but can hand out existing instances of its class. However, subclasses will see that C is not a constructor in such a class because they will not be able to invoke C’s pseudo-constructor from their constructors.

If a class C does not define the default constructor C.C, the default constructor C.C is automatically defined; that constructor takes the arguments that C’s superclass’s default constructor B.B takes. C.C calls B.B and then initializes C’s new instance members to their default values.

Calling a Superconstructor

Let C be a class and B its superclass. Any constructor C.n must call a constructor B.m or C.m before it accesses this or super or before it returns. The call can be either explicit or implicit; if C.n does not contain any calls to a constructor B.m or C.m, then a call to B.B with no arguments is automatically inserted as the first statement of C.n. The constructor C.n does not have to call another constructor if it exits by throwing an exception. C.n may not make a constructor call more than once.

A constructor C.n can call another constructor using one of the following statements:

super(args) Calls B’s default constructor B.B.
super.m(args)   Calls B’s constructor B.m. m must be a QualifiedIdentifier that names one of B’s constructors.
this(args) Calls C’s default constructor C.C.
this.m(args) Calls C’s constructor C.m. m must be a QualifiedIdentifier that names one of C’s constructors.

The above must be complete statements, not subexpressions of larger expressions. The first of the four forms above is unambiguously a SuperStatement, but the remaining three are parsed as ExpressionStatements. The following rules indicate whether one of these three forms (super.m(args), this(args), or this.m(args)) is treated as an expression or a call to a constructor:

  • If the form is part of a larger expression (for example, this(3,4)+5), then it is an expression.
  • If the form is not located inside a constructor (excluding nested functions), then it is an expression.
  • If m in super.m(args) does not name one of the superclass’s constructors, then the form is an expression (that, in this case, looks up the superclass’s property m on this and invokes it as a method with the arguments args).
  • If m in this.m(args) does not name one of the current class’s constructors, then the form is an expression (that, in this case, looks up the property m on this and invokes it as a method with the arguments args).
  • Otherwise, the form is a constructor call.

It is not possible to skip class hierarchy levels while constructing an object — if C’s superclass is B and B’s superclass is A, then one of C’s constructors may not directly call one of A’s constructors.

Class Extensions

A class extension is the ability to add methods to a class defined somewhere else. Class extensions were part of the ECMAScript 4 proposal but were dropped to keep the language simple; although useful, they significantly complicated the design of classes and introduced some pesky problems such as what to do when a package P first imports a base class C from package Q, then defines a subclass D of C, and finally extends C with an extension with the same name and namespace as a method of D.

The proposed class extensions were created using the extend attribute described below.

extend Attribute

The extend attribute takes a parameter C, which should be a compile-time constant expression that evaluates to a class, and adds the definition as a new member of class C. This allows one to add a method to an existing class C even if C is in an already defined package P. There are several restrictions:

  • The extend attribute can only be used on function, const, class, or namespace definitions, or on static var definitions. Thus, a class extension cannot add new instance variables to objects of class C; it can, however, add getters and setters. Operators cannot be defined in class extensions.
  • The new member must be either static or final, and it cannot override any existing member. If the new member is a function, the default is final.
  • The new member must be defined in a namespace that has been defined in the current package. In particular, it means that the new member cannot be public, because the public namespace is predefined by the system and not by any package. The default namespace is internal, which makes the new member of C visible only from inside the current package.
  • The new member does not get any special access privileges granted to C’s indigenous members. For example, it cannot see C’s private members, and it cannot see P’s internal members if P is not the current package. On the other hand, it can see the current package’s internal members.

The following example indicates adding methods to the system class String, using a newly created namespace StringExtension:

namespace StringExtension;

StringExtension extend(String) function scramble():String {...}
StringExtension extend(String) function unscramble():String {...}

use namespace(StringExtension);

var x:String = "abc".scramble();

Once the class extension is evaluated, methods scramble and unscramble become available on all strings in code within the scope of a use namespace(StringExtension). There is no possibility of name clashes with extensions of class String in other, unrelated packages because the names scramble and unscramble only acquire their special meanings when qualified by the namespace StringExtension.

Unless one desires to export a class extension to other packages, the default namespace internal generally works best and simplifies the above example to:

extend(String) {
  function scramble():String {...}
  function unscramble():String {...}
}

var x:String = "abc".scramble();

Interfaces

Interfaces were considered for ECMAScript 4 but were dropped to keep the language simple — they are not needed as much in a dynamically typed language than in a statically typed one.

Interfaces might be defined by adding the syntax and semantics below.

Interface Definition

InterfaceDefinition 
   interface Identifier ExtendsList Block
|  interface Identifier Semicolon
ExtendsList 
   «empty»
|  extends TypeExpressionList
TypeExpressionList 
   TypeExpressionallowIn

Interfaces behave much like classes except that an interface I is not a supertype of a class C that implements I. Instead, an instance c of C may be implicitly coerced to type I, which creates an instance i of I. Implicitly coercing i to type C yields the original instance c. It is unspecified whether c == i.

An interface may have both concrete and abstract members, but it may not have constructors.

In the absence of name conflicts, an interface I’s members may be accessed as properties of any instance c of a class C that implements I. However, it is legal to define an interface I with a member m with the same name as a member of class C and yet have the two members be different. It is also legal for a class to implement two interfaces I and J both of which have a member named m and have the two m’s remain distinct. Which one gets extracted when one performs the property lookup operation c.m depends on whether c was last coerced to one of the interfaces or to an object type.

Class Definitions

The Inheritance clause of class definitions would be modified to accommodate interfaces, as listed in an implements clause:

ClassDefinition  class Identifier Inheritance Block
Inheritance 
   «empty»
|  extends TypeExpressionallowIn
|  implements TypeExpressionList
|  extends TypeExpressionallowIn implements TypeExpressionList

The newly defined class inherits instance and static members from the superclass and superinterfaces, if any. In the case of a conflict between a superclass and a superinterface, the superclass prevails. In the case of a conflict between two superinterfaces, neither is preferred and the lookup has to be qualified by the name of the superinterface for instance members or done directly on one of the superinterfaces for static members.

Attributes

primitive

A primitive class modifier attribute was considered. This attribute would exclude null from the set of values that can be stored in variables typed with this class: if a class C is defined using the primitive attribute, then null is not considered to be a member of the type C (there would have to be a way to specify a default value for uninitialized variables of type C). This attribute would permit user-defined classes to behave like some predefined classes such as Number.

This attribute was dropped for now because of circularity problems with classes that haven’t been defined yet. If class C hasn’t been defined yet, one can still create a variable of type C; such a variable is initialized to null. If C turned out to be a primitive class then the variable’s value would need to be retroactively changed.

Importing Packages

The import directive can be extended with more options for better identifier and namespace management, as described below:

A package P can reference another package Q via an import directive:

ImportDirective 
ImportBinding 
IncludesExcludes 
   «empty»
|  , exclude ( NamePatterns )
|  , include ( NamePatterns )
NamePatterns 
   «empty»
NamePatternList 

If provided, ParenListExpression should be a list of namespaces provided by the package. These namespaces are used by the import statement. In order to resolve name conflicts between packages, IncludesExcludes provides finer-grain control over which names are imported. include or exclude clauses specify which sets of names are shared as top-level variables. If include is used, only the listed names are made accessible; if exclude is used, all names except the listed ones are made accessible. For example:

package My.P1 {
  explicit namespace N;

  N const a = "global a";
  N const b = "global b";
  N class C {

    static var x = 2;
  }
  N const c = new C(i:5);     // Initializes c.i to 5
  const x = "global x";
}

package My.P2 {
  import P = My.P1, namespace(N), exclude(N::b, x);  // Imports My.P1 and uses namespace N, excluding N::b and  x
  c;                          // OK; evaluates to the instance of class C
  N;                          // Error: N not found because it’s explicit
  P.N;                        // OK; evaluates to namespace N in package My.P1
  a;                          // OK; evaluates to "global a"
  b;                          // Error: N::b not found because it’s excluded
  P.b;                        // OK; evaluates to "global b"
  (P.N)::b;                   // Error: N::b not found because it’s excluded
  x;                          // Error: the global x not found because it’s excluded
  C.x;                        // OK; evaluates to 2
}

If no include or exclude clause is listed, the effect is the same as if exclude() were listed.

An import directive does the following:

  • Locate the target package specified by PackageName. If the package has not yet been loaded, then load it and wait until the target package’s Block is done evaluating. If loading the target package causes an import of the current package then throw a package circularity exception.
  • Let P be the target package object.
  • If Identifier is given, const-bind it to P in the current scope.
  • For each non-explicit top-level definition N::n (n in namespace N) in P, if N::n is excluded by the given IncludesExcludes, then skip that definition; otherwise, bind an alias N::n to P’s N::n in the global scope unless N::n is already defined in the global scope.
  • If ParenListExpression is provided, evaluate each expression E in ParenListExpression, looking up each free identifier in E as though it were prefixed with P.. Each such expression E should evaluate to a namespace S. Evaluate use namespace(S) using the given IncludesExcludes.

If package P has a public top-level definition n and package Q imports P using import PkgP = P, then package Q can refer to n as either n or PkgP.n. The shorter form n is not available if it conflicts with some other n. To avoid polluting its top-level scope, package Q can import package P using either import PkgP = P, include() or import PkgP = P, exclude(n), in which case package Q can refer to n only as PkgP.n.

If package P has an explicit top-level definition n and package Q imports P, then package Q can refer to that n only as PkgP.n.

If package P has a top-level definition n in namespace N and package Q imports P using import PkgP = P, then package Q can refer to n as either PkgP.N::n or N::n (in either of these the name N has to be accessible as well, which may require qualifying it if the accessibility of N is not public or using (PkgP.N) instead of N if the accessibility of N is explicit). Package Q can instead import P using import PkgP = P, namespace(N) to be able to refer to n as plain n, barring name collisions. Alternatively, package Q can execute import PkgP = P followed by use namespace(N) (or use namespace(PkgP.N)) to achieve the same effect.

Wrap Mode

Earlier drafts of this proposal had a wrap pragma that caused implicit coercions of an out-of-range integer to an integral machine type to wrap around instead of generating an error. The pragma would only affect the portions of the program in which the pragma was lexically in effect. The basic effect of this pragma would be to make arithmetic on sbyte, byte, short, ushort, int, uint, long, and ulong wrap around instead of generating errors when the result doesn’t fit in the destination.

Although useful for performance-critical code, this pragma was dropped to keep the language simple. Wrap-around can still be achieved using an explicit cast to one of the integral machine types.


ECMAScript 4 Netscape Proposal
Compatibility
previousup

Friday, March 14, 2003

ECMAScript 4 is intended to be upwards compatible with almost all ECMAScript 3 and earlier scripts. The following are the current compatibility issues:

  • Commas are now significant inside brackets, so expr[expr, expr] should be replaced by expr[(expr, expr)].
  • Initializers are not allowed on a for-in variable; the =b in the statement for (var a=b in c) ... is not allowed.
  • Uses of the identifiers as, is, namespace, and use need to be renamed or escaped with \_ because these are now reserved words.
  • ECMAScript 4 drops the wrapper classes Boolean, Number, and String. Boolean, Number, and String now refer to classes that have primitive booleans, numbers, and strings as instances. The methods of these new classes correspond to the methods of ECMAScript 3’s wrapper classes. The results of calling new on Boolean, Number, or String are now implementation-defined, so an implementation may choose to retain the wrappers for compatibility with ECMAScript 3, but it is not required to do so.
  • ECMAScript 4 permits but does not require implementations to allow array indices greater than 4294967295. Code that relies on indices greater than 4294967295 not being recognized as valid array indices may not work.
  • Code that modifies the standard ECMAScript 3 objects such as Object and String may not work.
  • ECMAScript 4 defines additional global constants, which may clash with top-level identifiers in programs.
  • Invalid ECMAScript 3 code may parse as valid ECMAScript 4 code. ECMAScript 3 programs which relied on getting an exception for such formerly invalid code may not work.

For applications such as browsers where 100% compatibility is needed, scripts will be assumed to be written ECMAScript 3 unless explicitly marked as being ECMAScript 4 through either an HTML or XML attribute or by including a use ecmascript(4) statement.


Waldemar Horwat
Last modified Monday, June 30, 2003
up