Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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.
*
Expand Down Expand Up @@ -3944,6 +3948,7 @@ private void debuggerStatement() {
* <pre>
* PrimaryExpression :
* this
* %
* IdentifierReference
* Literal
* ArrayLiteral
Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -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();
}
Expand Down Expand Up @@ -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) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,8 @@ class ParserContextFunctionNode extends ParserContextBaseNode {
private List<Map.Entry<VarNode, Scope>> hoistedVarDeclarations;
private List<Map.Entry<VarNode, Scope>> hoistableBlockFunctionDeclarations;

private int pipeDepth = 0;

/**
* @param token The token for the function
* @param ident External function name
Expand Down Expand Up @@ -741,4 +743,17 @@ private static int calculateLength(final List<IdentNode> parameters) {
}
return length;
}
public int getPipeDepth(){
return pipeDepth;
}

public void increasePipeDepth(){
pipeDepth++;
}

public void decreasePipeDepth(){
if(pipeDepth > 0){
pipeDepth--;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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, "..."),
Expand Down
123 changes: 123 additions & 0 deletions graal-js/src/com.oracle.truffle.js.test/js/pipeline.js
Original file line number Diff line number Diff line change
@@ -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);