Stage 0 Draft / July 7, 2020

Hack Pipelines

Introduction

This is the formal specification for a proposed "pipe operator" |> in JavaScript. It modifies the original ECMAScript specification with several new or revised clauses. The Core Proposal is at Stage 0. Additional Features are given as annexes. See the proposal's explainer for the proposal's background, motivation, usage examples, explanation, and information on planned add-on proposals.

1Executable Code and Execution Contexts

1.1Lexical Environments

1.1.1Lexical Topics

Editor's Note

This section is a wholly new sub-clause of the original Lexical Environments clause.

The topic binding of a Lexical Environment immutably binds the topic reference # to one value of any ECMAScript language type (called the topic value or simply the topic), within that Lexical Environment, at the time of the Lexical Environment's instantiation. The topic of a Lexical Environment conceptually serves as the value that the lexical context is "about".

A topic-binding environment is a Lexical Environment that establishes a topic binding. The topic environment of the running execution context is its Lexical Environment's nearest outer environment that is a topic-binding environment, as defined by the abstract operator GetTopicEnvironment.

Note

The only Lexical Environments that are topic-binding environments are declarative environments that are associated with PipelineStep and which were created with the TopicEnvironmentInstantiation abstract operation.

There is also one syntax nonterminal that instantiates topic-binding environments with provided topic value of any ECMAScript language type. This topic-binding nonterminal is ConditionalExpression in PipelineStep:ConditionalExpression , which may use the abstract operation TopicEnvironmentInstantiation. In addition, PipelineStep hides its own inner topic references from Contains. Within the scope of PipelineStep:ConditionalExpression , such topic references would not trigger early error rules during program compilation. Instead, they would be evaluated at runtime into the value of the newly instantiated topic-binding environments' topic binding.

All other Lexical Environments do not establish any topic binding; in particular, object and global environments are never topic-binding environments.

In addition, several syntax nonterminals associated with Lexical Environments are associated with early error rules that forbid their containing the topic reference #, except where the topic reference is within PipelineStep:ConditionalExpression . These topic-forbidding nonterminals are:

Top-level program scopes:
ScriptBody from Script:ScriptBody .
ModuleBody from Module:ModuleBody .
Inner program scopes that establish lexical bindings, notably:
Any FunctionStatementList from FunctionBody:FunctionStatementList .

Any use of the topic reference within these nonterminals (as detected by the static semantic rule Contains) would trigger early error rules associated with their productions: during program compilation, before runtime evaluation ever would have begun.

1.1.2Environment Records

Editor's Note

This section augments the original Environment Records clause.

Table 1: Abstract Methods of Environment Records
Method Purpose
HasTopicBinding() Determine the status of an Environment Record's topic binding. Return true if it establishes a topic binding and false if it does not.

1.1.2.1Declarative Environment Records

Editor's Note

Each declarative Environment Record is associated with an ECMAScript program scope containing variable, constant, let, class, module, import, and/or function declarations. A declarative Environment Record binds the set of identifiers defined by the declarations contained within its scope.

Declarative Environment Records have the additional state fields listed in Table 2.

Table 2: Additional Fields of Declarative Environment Records
Method Value Purpose
[[TopicBindingStatus]] false | true If [[TopicBindingStatus]]'s value is true, the Environment Record binds the Environment Record establishes its environment's topic binding (that is, it binds #). If the value is false, the Environment Record has no topic binding. [[TopicBindingStatus]]'s default value is false. Its value may be changed from false to true but never vice versa.
[[TopicValue]] any | empty If the value of [[TopicBindingStatus]] is true, [[TopicValue]] is the environment's topic value (that is, the value of # within its program scope). Otherwise, the value of [[TopicValue]] is empty.

Declarative Environment Records support all of the abstract methods of Environment Records listed in Table 1. In addition, declarative Environment Records support the methods listed in Table 3.

Table 3: Additional Methods of Declarative Environment Records
Method Purpose
BindTopicValue(V) Establish the immutable topic binding of this Environment Record and set the topic binding's value. V is the topic value and is a value of any ECMAScript language type. Afterward, the value returned by the Environment Record's HasTopicBinding method is true. This method cannot be called more than once on any single Environment Record.

The behaviour of the concrete and additional specification methods for declarative Environment Records is defined by the following algorithms.

1.1.2.1.1HasTopicBinding ( )

Editor's Note

This section is a wholly new sub-clause of the original Declarative Environment Records clause.

The concrete Environment Record method HasTopicBinding for declarative Environment Records returns the value of the record's field [[TopicBindingStatus]], which is false by default. The value may instead be true if its BindTopicValue method has been called.

  1. Let envRec be the function Environment Record for which the method was invoked.
  2. Return envRec.[[TopicBindingStatus]].

1.1.2.1.2BindTopicValue ( V )

Editor's Note

This section is a wholly new sub-clause of the original Declarative Environment Records clause.

The method BindTopicValue for declarative Environment Records is guaranteed to be called only when the Environment Records do not yet have established topic bindings.

  1. Let envRec be the declarative Environment Record for which the method was invoked.
  2. Assert: envRec.[[TopicBindingStatus]] is false.
  3. Set envRec.[[TopicValue]] to V.
  4. Set envRec.[[TopicBindingStatus]] to true.
  5. Return NormalCompletion(empty).

1.1.2.2Object Environment Records

1.1.2.2.1HasTopicBinding ( )

Editor's Note

This section is a wholly new sub-clause of the original Object Environment Records clause.

Regular object Environment Records never have topic bindings.

  1. Return false.

1.1.2.3Global Environment Records

1.1.2.3.1HasTopicBinding ( )

Editor's Note

This section is a wholly new sub-clause of the original Global Environment Records clause.

Global Environment Records never have a topic binding.

  1. Return false.

2ECMAScript Language: Lexical Grammar

2.1Punctuators

Editor's Note

This section augments the original Punctuators clause.

Punctuator::one of{()[]....;,<><=>===!====!==+-*%**++--<<>>>>>&|^!~&&||?:|>#=+=-=*=%=**=<<=>>=>>>=&=|=^==> Punctuator::one of{()[]....;,<><=>===!====!==+-*%**++--<<>>>>>&|^!~&&||?:=+=-=*=%=**=<<=>>=>>>=&=|=^==>

3ECMAScript Language: Expressions

3.1Primary Expression

Editor's Note

This section augments the original Primary Expression clause.

Syntax

PrimaryExpression[Yield, Await]:this # IdentifierReference[?Yield, ?Await] Literal ArrayLiteral[?Yield, ?Await] ObjectLiteral[?Yield, ?Await] FunctionExpression ClassExpression[?Yield, ?Await] GeneratorExpression AsyncFunctionExpression AsyncGeneratorExpression RegularExpressionLiteral TemplateLiteral[?Yield, ?Await, ~Tagged] CoverParenthesizedExpressionAndArrowParameterList[?Yield, ?Await]

3.1.1The Topic Reference

Editor's Note

This section is a wholly new sub-clause to be inserted between the original this Keyword clause and the original Identifier Reference clause.

Note

The topic reference, which is the token #, is a nullary operator that evaluates to the value of the current Lexical Environment's topic. The topic reference acts as if it were a special variable: implicitly bound to the topic value, yet still lexically scoped. But # is not actually an IdentifierName and the topic reference is not a variable, and it cannot be bound by typical assignment; instead, it is immutably bound to a value during the instantiation of certain topic-binding environments.

The concept of lexical topic binding is further discussed in Lexical Topics and in Declarative Environment Records.

3.1.1.1Runtime Semantics: GetTopicEnvironment

Note 1

GetTopicEnvironment finds the Environment Record that currently supplies the topic binding (the binding of #). That is, it finds the running execution topic's nearest-ancestral topic-binding environment, which is the nearest-ancestral outer environment that has a topic binding status of true. It is never called when there is no topic environment.

When the abstract operation GetTopicEnvironment is called the following steps are performed:

  1. Let lex be the running execution context's Lexical Environment.
  2. Repeat,
    1. Let envRec be lex's Environment Record.
    2. Let status be envRec.HasTopicBinding().
    3. If status is true, return envRec.
    4. Assert: lex is not a global environment.
    5. Let outer be the value of lex's outer environment reference.
    6. Set lex to outer.
  3. Return lex.
Note 2

The loop in step 2 will always terminate because the List of environments will always end before reaching the global environment. GetTopicEnvironment will never be called when there is no topic-binding environment in the List of environments.

This is because how, in general, syntax nonterminals that define top-level scopes (such as Script and Module) are syntactically forbidden to contain the topic reference #. Any use of the topic reference within those nonterminals (as detected by the static semantic rule Contains) would trigger early error rules associated with those nonterminals. It is only within nonterminals that hide the topic reference from Contains such as ConditionalExpression in PipelineStep:ConditionalExpression that the topic reference is syntactically permitted.

3.1.1.2Runtime Semantics: GetTopicValue

Note

GetTopicValue gets the value of the topic environment’s current topic binding. It is never called when there is no topic environment.

When the abstract operation GetTopicEnvironment is called the following steps are performed:

  1. Let envRec be GetTopicEnvironment().
  2. Assert: envRec is a declarative Environment Record.
  3. Assert: envRec.HasTopicBinding() is true.
  4. Let topicValue be envRec.[[TopicValue]].
  5. Return topicValue.

3.1.1.3Runtime Semantics: Evaluation

PrimaryExpression:#
  1. Return GetTopicValue().

3.2Pipe Operator

Editor's Note

This section is a wholly new sub-clause to be inserted between the original Conditional Operator (? :) clause and the original Assignment Operators clause.

Syntax

PipeExpression[In, Yield, Await]:ConditionalExpression[?In, ?Yield, ?Await] ConditionalExpression[?In, ?Yield, ?Await]|>Pipeline[?In, ?Yield, ?Await] Pipeline[In, Yield, Await]:PipeLineStep[?In, ?Yield, ?Await] PipeLineStep[?In, ?Yield, ?Await]|>Pipeline[?In, ?Yield, ?Await] PipeLineStep[In, Yield, Await]:ConditionalExpression[?In, ?Yield, ?Await]

3.2.1Static Semantics: Early Errors

Editor's Note

This section is a wholly new sub-clause.

PipelineStep:ConditionalExpression
  • It is a Syntax Error if ConditionalExpression Contains # is false.

3.2.2Runtime Semantics: TopicEnvironmentInstantiation

Editor's Note

This section is a wholly new sub-clause.

Note

This abstract operation constructs, instantiates, then returns a new declarative Lexical Environment for a topic-style pipeline step. It creates an immutable topic binding in that declarative environment using the given topicValue.

TopicPipelineInstantiation is performed as follows using arguments env and topicValue.

  1. Let envRec be env's Environment Record.
  2. Assert: envRec is a declarative Environment Record.
  3. Assert: envRec.HasTopicBinding() is false.
  4. Perform ! envRec.BindTopicValue(topicValue).

3.2.3Runtime Semantics: PipelineEvaluation

With parameter inputValue.

PipelineStep:ConditionalExpression
  1. Let oldEnv be the running execution context's Lexical Environment.
  2. Let pipelineStepEnv be New Declarative Environment(oldEnv).
  3. Perform TopicEnvironmentInstantiation(inputValue, pipelineStepEnv).
  4. Set the running execution context's Lexical Environment to pipelineStepEnv.
  5. Let pipelineValue be the result of evaluating ConditionalExpression.
  6. Set the running execution context's Lexical Environment to oldEnv.
  7. Return pipelineValue.
Pipeline:PipeLineStep|>Pipeline
  1. Let stepOutputValue be ? PipelineEvaluation of PipeLineStep with argument inputValue.
  2. Let pipelineRemainderOutputRef be ? PipelineEvaluation of Pipeline with argument stepOutputValue.
  3. Return ? GetValue(pipelineRemainderOutputRef).

3.2.4Runtime Semantics: Evaluation

PipeExpression:ConditionalExpression|>Pipeline
  1. Let inputRef be the result of evaluating ConditionalExpression.
  2. Let inputValue be the result of ? GetValue(inputRef).
  3. Let pipelineOutputValue be ? PipelineEvaluation of Pipeline with argument inputValue.
  4. Return pipelineOutputValue.

3.3Assignment Operators

Editor's Note

This section augments the original Assignment Operators clause.

Syntax

AssignmentExpression[In, Yield, Await]:ConditionalExpression[?In, ?Yield, ?Await] PipeExpression[?In, ?Yield, ?Await] [+Yield]YieldExpression[?In, ?Await] ArrowFunction[?In, ?Yield, ?Await] AsyncArrowFunction[?In, ?Yield, ?Await] LeftHandSideExpression[?Yield, ?Await]=AssignmentExpression[?In, ?Yield, ?Await] LeftHandSideExpression[?Yield, ?Await]AssignmentOperatorAssignmentExpression[?In, ?Yield, ?Await]

4ECMAScript Language: Functions and Classes

4.1Function Definitions

4.1.1Static Semantics: Early Errors

Editor's Note FunctionBody:FunctionStatementList

4.2Arrow Function Definitions

4.2.1Static Semantics: Contains

With parameter symbol.

Editor's Note ArrowFunction:ArrowParameters=>ConciseBody
  1. If symbol is not one of NewTarget, SuperProperty, SuperCall, super or this super, this, or #, return false.
  2. If ArrowParameters Contains symbol is true, return true.
  3. If symbol is #, return false.
  4. Return ConciseBody Contains symbol.
Note
Normally, Contains does not look inside most function forms. However, Contains is used to detect new.target, this, and superthis, super, and # usage within an ArrowFunction.

5ECMAScript Language: Scripts and Modules

5.1Scripts

5.1.1Static Semantics: Early Errors

Editor's Note

This section augments the original Scripts, Static Semantics: Early Errors clause. It is not planned to be removed by other additional syntax.

ScriptBody:StatementList Note

In general, a top-level program scope, including Script, may not contain a topic reference #. However, such topic-forbidding nonterminals may also contain topic-binding nonterminals such as ConditionalExpression in PipelineStep:ConditionalExpression , which would, in turn, hide their own inner topic references from the Contains abstract operation. Within those topic-binding nonterminals, such topic references would therefore not trigger the early error above.

5.2Modules

5.2.1Static Semantics: Early Errors

Editor's Note

This section augments the original Modules, Static Semantics: Early Errors clause. It is not planned to be removed by other additional syntax.

ModuleItem:StatementListItem Note

In general, a top-level program scope, including Module, may not contain a topic reference #. However, such topic-forbidding nonterminals may also contain topic-binding nonterminals such as ConditionalExpression in PipelineStep:ConditionalExpression , which would, in turn, hide their own inner topic references from the Contains abstract operation. Within those topic-binding nonterminals, such topic references would therefore not trigger the early error above.

AAdditional Feature OP: Optional Pipelines

This will be introduced as its own separate proposal but is included here for reference.

This annex specifies additional ECMAScript language syntax and semantics for optional pipelines. It augments the core proposal by introducing an additional "optional pipe operator" ?>, which executes its associated step only when its input value is not null or undefined. It is analogous to optional chains.

See the explainer document, § Motivation, Additional Feature OP for information on this feature's motivation.

The rest of this additional proposal spec is WIP.

BCopyright & Software License

Copyright Notice

© 2020 Michal Srb (original by J. S. Choi), Ecma International

Software License

All Software contained in this document ("Software") is protected by copyright and is being made available under the "BSD License", included below. This Software may be subject to third party rights (rights from parties other than Ecma International), including patent rights, and no licenses under such third party rights are granted under this license even if the third party concerned is a member of Ecma International. SEE THE ECMA CODE OF CONDUCT IN PATENT MATTERS AVAILABLE AT http://www.ecma-international.org/memento/codeofconduct.htm FOR INFORMATION REGARDING THE LICENSING OF PATENT CLAIMS THAT ARE REQUIRED TO IMPLEMENT ECMA INTERNATIONAL STANDARDS.

Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:

  1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
  2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
  3. Neither the name of the authors nor Ecma International may be used to endorse or promote products derived from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE ECMA INTERNATIONAL "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL ECMA INTERNATIONAL BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.