Google Dart Language Specification I
[The current version (0.05) was released November 14, 2011. See the specification’s change log (section 1.2) for a list of differences between versions]
Changes Since Version 0.02
The following changes have been made in version 0.03 since version 0.02. In addition, various typographical errors have been corrected. The changes are listed by section number.
2: Expanded examples of grammar in notation section.
7.9: Removed static warning when imported superinterface of a class contains private members.
8.3: Removed redundant prohibition on default values.
8.4: Removed static warning when imported superinterface of an interface contains private members.
10: Fixed typo in grammar
10.10.2: make clear that referenced constructor must be marked const.
10.14.3: fixed botched sentence where superclass S is introduced.
10.27: qualified definition of v++ so it is clear that v is an identifier.
Changes Since Version 0.03
7.1: Added rules prohibiting clashes of inherited variable names or of static and instance methods.
9: Added prohibition against cyclic type hierarchy for type parameters.
10.10: Clarified requirements on use of parameterized types in instance creation expressions.
10.13.2: Added requirement that qi are distinct.
10.4.2: Static method invocation determines the function (which may involve evaluating a getter) before evaluating the arguments, so that static invocation and top-level function invocation agree.
10:30: Added missing test that type being tested against is in scope and is indeed a type.
11.5.1: Changed for loop to introduce fresh variable for each iteration.
13.8: Malformed parameterized types generate warnings, not errors(except when used in reified contexts like instance creation and superclasses/interfaces).
Changes Since Version 0.04
7.1.2: Removed unary plus operator. Clarified that operator formals must be required.
7.5.3: Filled in a lot of missing detail.
8.3: Allowed factory class to be declared via a qualified name.
10.3: Changed production for Number.
10.10.2: Added requirements that actuals be constant, rules for dealing with inappropriate types of actuals, and examples. Also explicitly prohibit type variables.
10.13.4: Modified final bullet to keep it inline with similar clauses in other sections. Exact wording of these sections also tweaked slightly.
14.1: Specified unicode form of Dart source.
We distinguish between normative and non-normative text. Normative text defines the rules of Dart. It is given in this font (black Arial 11pt). At this time, non-normative text includes:
- Rationale. Discussion of the motivation for language design decisions appears in blue italics. Distinguishing normative from non-normative helps clarify what part of the text is binding and what part is merely expository.
- Commentary, given in green (Arial, 11pt) . Comments such as “The careful reader will have noticed that the name Dart has four characters” serve to illustrate or clarify the specification, but are redundant with the normative text. The difference between commentary and rationale can be subtle. Commentary is more general than rationale, and may include illustrative examples or clarifications.
- Open questions (in red). Open questions are points that are unsettled in the mind of the author(s) of the specification; expect them (the questions, not the authors; precision is important in a specification) to be eliminated in the final specification. Should the text at the end of the previous bullet be rationale or commentary?
Reserved words and built-in identifiers appear in this font.
Examples would be switch or class.
Grammar productions are given in a common variant of EBNF. The left
hand side of a production ends with a colon. On the right hand side, alternation is represented by vertical bars, and sequencing by spacing. Optional elements of a production are suffixed by a question mark like so: anElephant? . Appending a star to an element of a production means it may be repeated zero or more times. Appending a plus sign to a production means it occurs one or more times. Parentheses are used for grouping. Negation (the not combinator of PEGs) is represented by prefixing an element of a production with a tilde.
An example would be:
| OneThing After Another
| (Some Grouped Things)
Both syntactic and lexical productions are represented this way. Lexical productions are distinguished by their names. The names of lexical productions consist exclusively of upper case characters and underscores. As always, within grammatical productions, whitespace and comments between elements of the production are implicitly ignored unless stated otherwise.
Productions are embedded, as much as possible, in the discussion of the constructs they represent.
A list x1,…, xn denotes any list of n elements of the form xi, 1 <= i <= n. Note that n may be zero, in which case the list is empty. We use such lists extensively throughout this specification.
The notation [x1, …, xn/y1, …, yn]E denotes a copy of E in which all occurrences of xi, 1 <= i <= n have been replaced with yi.
The specifications of operators often involve statements such as x op y is equivalent to the method invocation x.op(y). Such specifications should be understood as a shorthand for:
x op y is equivalent to the method invocation x.op’(y), assuming the class of x actually declared a non-operator method named op’ defining the same function as the operator op. This circumlocution is required because x.op(y), where op is an operator, is not legal syntax. However, it is painfully verbose, and we prefer to state this rule once here, and use a concise and clear notation across the specification.
When the specification refers to the order given in the program, it means the order of the program source code text, scanning left-to-right and top-to-bottom.
Dart is a class-based, single-inheritance, pure object-oriented programming language. Dart is optionally typed and supports reified generics and interfaces.
Dart programs can be statically checked. The static checker will report some violations of the type rules, but such violations do not abort compilation or preclude execution.
Dart programs may be executed in one of two modes: production mode or checked mode. In production mode, static type annotations have absolutely no effect on execution. In checked mode, assignments are dynamically checked, and certain violations of the type system raise exceptions at run time.
The coexistence between optional typing and reification is based on the following:
- Reified type information reflects the types of objects at runtime and may always be queried by dynamic typechecking constructs (the analogs of instanceOf, casts, typecase etc. in other languages). Reified type information includes class and interface declarations, the class of an object, and type arguments to constructors.
- Static type annotations determine the types of variables and function declarations (including methods and constructors).
- Production mode respects optional typing. Static type annotations do not affect runtime behavior.
- Checked mode utilizes static type annotations and dynamic type information aggressively yet selectively to provide early error detection during development.
Dart programs are organized in a modular fashion into units called libraries. Libraries are units of encapsulation and may be mutually recursive.
However they are not first class. To get multiple copies of a library running simultaneously, one needs to spawn an isolate.
Dart is lexically scoped and uses a single namespace for variables, functions and types. It is a compile-time error if there is more than one entity with the same name declared in the same scope. Names in inner scopes may hide names in enclosing scopes, however, it is a static warning if a declaration introduces a name that is available in a lexically enclosing scope.
Names may be introduced into a scope by declarations within the scope or by other mechanisms such as imports or inheritance.
Dart supports two levels of privacy: public and private. A declaration is private if it begins with an underscore (the _ character) otherwise it is public.
A declaration m is accessible to library L if m is declared in L or if m is public.
Private declarations may only be accessed within the library in which they are declared.
Privacy is, at this point, a static notion tied to a particular piece of code (a library). It is designed to support software engineering concerns rather than security concerns. Untrusted code should always run in an another isolate. It is possible that libraries will become first class objects and privacy will be a dynamic notion tied to a library instance.
Privacy is indicated by the name of a declaration – hence privacy and naming are not orthogonal. This has the advantage that both humans and machines can recognize access to private declarations at the point of use without knowledge of the context from which the declaration is derived.
Dart code is always single threaded. There is no shared-state concurrency in Dart. Concurrency is supported via actor-like entities called isolates.
An isolate is a unit of concurrency. It has its own memory and its own thread of control. Isolates communicate by message passing. No mutable state is ever shared between isolates. Isolates are created by spawning.
Errors and Warnings
This specification distinguishes between several kinds of errors.
Compile-time errors are errors that preclude execution. A compile time error must be reported by a Dart compiler before the erroneous code is executed.
A Dart implementation has considerable freedom as to when compilation takes place. Modern programming language implementations often interleave compilation and execution, so that compilation of a method may be delayed, e.g., until it is first invoked. Consequently, compile-time errors in a method m may be reported as late as the time of m’s first invocation.
As a web language, Dart is often loaded directly from source, with no intermediate binary representation. In the interests of rapid loading, Dart implementations may choose to avoid full parsing of method bodies, for example. This can be done by tokenizing the input and checking for balanced curly braces on method body entry. In such an implementation, even syntax errors will be detected only when the method needs to be executed, at which time it will be compiled (JITed).
In a development environment a compiler should of course report compilation errors eagerly so as to best serve the programmer.
Static warnings are those warnings reported by the static checker. They have no effect on execution. Many, but not all, static warnings relate to types, in which case they are known as static type warnings. Static warnings must be provided by Dart compilers used during development.
Dynamic type errors are type errors reported in checked mode.
Run time errors are exceptions raised during execution. Whenever we say that an exception ex is raised or thrown, we mean that a throw statement of the form: throw ex; was implicitly executed. When we say that a C is thrown, where C is an exception class, we mean that an instance of class C is thrown.