diff --git a/graal-js/src/com.oracle.js.parser/src/com/oracle/js/parser/Parser.java b/graal-js/src/com.oracle.js.parser/src/com/oracle/js/parser/Parser.java index 7dc14bc8c7b..d852cd54804 100644 --- a/graal-js/src/com.oracle.js.parser/src/com/oracle/js/parser/Parser.java +++ b/graal-js/src/com.oracle.js.parser/src/com/oracle/js/parser/Parser.java @@ -81,9 +81,11 @@ import static com.oracle.js.parser.TokenType.LBRACKET; import static com.oracle.js.parser.TokenType.LET; import static com.oracle.js.parser.TokenType.LPAREN; +import static com.oracle.js.parser.TokenType.MOD; import static com.oracle.js.parser.TokenType.MUL; import static com.oracle.js.parser.TokenType.OF; import static com.oracle.js.parser.TokenType.PERIOD; +import static com.oracle.js.parser.TokenType.PIPELINE; import static com.oracle.js.parser.TokenType.PRIVATE_IDENT; import static com.oracle.js.parser.TokenType.RBRACE; import static com.oracle.js.parser.TokenType.RBRACKET; @@ -362,6 +364,8 @@ public class Parser extends AbstractParser { private boolean isModule; + private boolean topicReferenceUsed = false; + /** * Used to pass (async) arrow function flags from head to body. * @@ -3944,6 +3948,7 @@ private void debuggerStatement() { *
* PrimaryExpression :
* this
+ * %
* IdentifierReference
* Literal
* ArrayLiteral
@@ -4031,6 +4036,15 @@ private Expression primaryExpression(boolean yield, boolean await, CoverExpressi
TruffleString v8IntrinsicNameTS = lexer.stringIntern(v8IntrinsicName);
return createIdentNode(v8IntrinsicToken, ident.getFinish(), v8IntrinsicNameTS);
}
+ }else if(isES2023()){
+ int pipeDepth = lc.getCurrentFunction().getPipeDepth();
+ if(pipeDepth <= 0){
+ throw error(JSErrorType.SyntaxError, "The topic reference can not be used here!");
+ }
+ next();
+ addIdentifierReference("%" + pipeDepth);
+ topicReferenceUsed = true;
+ return new IdentNode(Token.recast(token, IDENT), finish + 1, lexer.stringIntern("%" + pipeDepth));
}
default:
@@ -6341,7 +6355,7 @@ private Expression expression(Expression exprLhs, int minPrecedence, boolean in,
int nextPrecedence = type.getPrecedence();
// Subtask greater precedence.
- while (type.isOperator(in) && (nextPrecedence > precedence || (nextPrecedence == precedence && !type.isLeftAssociative()))) {
+ while (type.isOperator(in) && (nextPrecedence > precedence || (nextPrecedence == precedence && !type.isLeftAssociative()))){
rhs = expression(rhs, nextPrecedence, in, yield, await);
nextPrecedence = type.getPrecedence();
}
@@ -6432,6 +6446,34 @@ private Expression assignmentExpression(boolean in, boolean yield, boolean await
popDefaultName();
}
}
+ } else if(type == PIPELINE && isES2023()) {
+ boolean prevRef = topicReferenceUsed;
+ topicReferenceUsed = false;
+ lc.getCurrentFunction().increasePipeDepth();
+ int pipeDepth = lc.getCurrentFunction().getPipeDepth();
+
+ next();
+
+ IdentNode placeHolder = new IdentNode(Token.recast(token, IDENT),
+ finish + 1, lexer.stringIntern("%" + pipeDepth));
+ BinaryNode lhs = new BinaryNode(Token.recast(token, ASSIGN), placeHolder, exprLhs);
+ Expression rhs = assignmentExpression(in, yield, await);
+
+ if(isStrictMode){
+ final VarNode var = new VarNode(line, Token.recast(token, LET), placeHolder.getFinish(), placeHolder.setIsDeclaredHere(), null);
+ declareVar(lc.getCurrentScope(), var);
+ }
+
+ if(!topicReferenceUsed){
+ throw error("Pipe body must contain the topic reference token(%) at least once");
+ }
+
+ lc.getCurrentFunction().decreasePipeDepth();
+
+ BinaryNode result = new BinaryNode(Token.recast(token, COMMARIGHT), lhs, rhs);
+ topicReferenceUsed = prevRef;
+
+ return result;
} else {
if (canBeAssignmentPattern) {
if (coverExpression != CoverExpressionError.DENY) {
diff --git a/graal-js/src/com.oracle.js.parser/src/com/oracle/js/parser/ParserContextFunctionNode.java b/graal-js/src/com.oracle.js.parser/src/com/oracle/js/parser/ParserContextFunctionNode.java
index fc05a754a68..98782bd7679 100644
--- a/graal-js/src/com.oracle.js.parser/src/com/oracle/js/parser/ParserContextFunctionNode.java
+++ b/graal-js/src/com.oracle.js.parser/src/com/oracle/js/parser/ParserContextFunctionNode.java
@@ -109,6 +109,8 @@ class ParserContextFunctionNode extends ParserContextBaseNode {
private List> hoistedVarDeclarations;
private List> hoistableBlockFunctionDeclarations;
+ private int pipeDepth = 0;
+
/**
* @param token The token for the function
* @param ident External function name
@@ -741,4 +743,17 @@ private static int calculateLength(final List parameters) {
}
return length;
}
+ public int getPipeDepth(){
+ return pipeDepth;
+ }
+
+ public void increasePipeDepth(){
+ pipeDepth++;
+ }
+
+ public void decreasePipeDepth(){
+ if(pipeDepth > 0){
+ pipeDepth--;
+ }
+ }
}
diff --git a/graal-js/src/com.oracle.js.parser/src/com/oracle/js/parser/TokenType.java b/graal-js/src/com.oracle.js.parser/src/com/oracle/js/parser/TokenType.java
index bb44b027cbb..f134332a484 100644
--- a/graal-js/src/com.oracle.js.parser/src/com/oracle/js/parser/TokenType.java
+++ b/graal-js/src/com.oracle.js.parser/src/com/oracle/js/parser/TokenType.java
@@ -122,6 +122,7 @@ public enum TokenType {
ASSIGN_BIT_OR (BINARY, "|=", 2, false),
OR (BINARY, "||", 4, true),
ASSIGN_OR (BINARY, "||=", 2, false, 12),
+ PIPELINE (BINARY, "|>", 2, true),
RBRACE (BRACKET, "}"),
BIT_NOT (UNARY, "~", 15, false),
ELLIPSIS (UNARY, "..."),
diff --git a/graal-js/src/com.oracle.truffle.js.test/js/pipeline.js b/graal-js/src/com.oracle.truffle.js.test/js/pipeline.js
new file mode 100644
index 00000000000..b12bab3679b
--- /dev/null
+++ b/graal-js/src/com.oracle.truffle.js.test/js/pipeline.js
@@ -0,0 +1,123 @@
+/**
+ * Pipeline operator proposal.
+ *
+ */
+
+/**
+*@option --ecmascript-version=staging
+*/
+
+load('assert.js');
+
+function double(number){
+ return number * 2;
+}
+
+function add(number1, number2){
+ return number1 + number2;
+}
+
+class Rectangle {
+ constructor(height, width) {
+ this.height = height;
+ this.width = width;
+ }
+
+ get area() {
+ return this.calcArea();
+ }
+
+ calcArea() {
+ return this.height * this.width;
+ }
+}
+
+const array = ['Apple', 'Orange', 'Strawberry'];
+
+let unaryFuncBody = 5 |> double(%);
+assertEqual(10, unaryFuncBody);
+
+let funcBody = double(3) |> add(%, 2);
+assertEqual(8, funcBody);
+
+let methodPipeBody = new Rectangle(2, 3) |> %.calcArea();
+assertEqual(6, methodPipeBody);
+
+let arithmetic = (14 * 4) / 2 |> % + 1;
+assertEqual(29, arithmetic);
+
+let arrayLiteral = array.indexOf('Orange') |> array[%];
+assertEqual('Orange', arrayLiteral);
+
+let arrayLiteral2 = 2 |> [1, %, 3];
+assertEqual(JSON.stringify([1, 2, 3]), JSON.stringify(arrayLiteral2));
+
+let objectLiteral = 2 * 3 |> { type: "rectangle", area : %};
+assertEqual(JSON.stringify({type: "rectangle", area: 6}), JSON.stringify(objectLiteral));
+
+let templateLiteral = array[2] |> `${%}`;
+assertEqual('Strawberry', templateLiteral);
+
+let construction = (8/2) |> new Rectangle(2, %);
+assertEqual(JSON.stringify(new Rectangle(2, 4)), JSON.stringify(construction));
+
+function resolveAfter2Seconds(x) {
+ return new Promise((resolve) => {
+ setTimeout(() => {
+ resolve(x);
+ }, 2000);
+ });
+}
+
+async function f1() {
+ const x = 10 |> await resolveAfter2Seconds(%);
+ console.log(x);
+}
+
+f1();
+
+//yield
+function* counter(value) {
+ while (true) {
+ const step = value++ |> yield %;
+
+ if (step) {
+ value += step;
+ }
+ }
+}
+
+const generatorFunc = counter(0);
+assertEqual(0, generatorFunc.next().value);
+assertEqual(1, generatorFunc.next().value);
+assertEqual(2, generatorFunc.next().value);
+
+//function body
+let funcExpression = function test(value){
+ let var1 = 4 + value;
+ let var2 = 7 |> % * var1;
+ console.log("Result: " + var2);
+} |> %(2);
+
+/*
+* Test chaining of pipeline
+*/
+
+const chainingExample1 = 7 |> new Rectangle(6, %) |> %.calcArea();
+assertEqual(42, chainingExample1);
+const chainingExample2 = 7 |> new Rectangle(6, %) |> %.calcArea() |> % % %;
+assertEqual(0, chainingExample2);
+const chainingExample3 = 7 |> new Rectangle(6, %) |> %.calcArea() |> % % 2 |> array[%];
+assertEqual('Apple', chainingExample3);
+const chainingExample4 = 7 |> new Rectangle(6, %) |> %.calcArea() |> % % 2 |> array[%] |> `${%}`;
+assertEqual('Apple', chainingExample4);
+const chainingExample5 = 7 |> new Rectangle(6, %) |> %.calcArea() |> % % 2 |> array[%] |> `${%}` |> array.indexOf(%);
+assertEqual(0, chainingExample5);
+
+/*
+* Error testing
+*/
+
+assertThrows(() => new Rectangle(2, 3) |> %.squareFootage(), TypeError);
+
+