diff --git a/src/lexer/tokens.js b/src/lexer/tokens.js index 638834da..7a54c205 100644 --- a/src/lexer/tokens.js +++ b/src/lexer/tokens.js @@ -342,6 +342,9 @@ module.exports = { } else if (nchar === "|") { this.input(); return this.tok.T_BOOLEAN_OR; + } else if (nchar === ">") { + this.input(); + return this.tok.T_PIPE; } return "|"; }, diff --git a/src/parser/expr.js b/src/parser/expr.js index 3d73a15f..6245ba12 100644 --- a/src/parser/expr.js +++ b/src/parser/expr.js @@ -119,6 +119,14 @@ module.exports = { if (this.token === this.tok.T_COALESCE) { return result("bin", "??", expr, this.next().read_expr()); } + // extra operations : + // $a = "Hi" |> strtoupper(...); + if (this.token === this.tok.T_PIPE) { + if (this.version < 805) { + this.raiseError("PHP 8.5+ is required to use pipe operator"); + } + return result("bin", "|>", expr, this.next().read_expr()); + } // extra operations : // $username = $_GET['user'] ? true : false; diff --git a/src/tokens.js b/src/tokens.js index fbb5e220..0b9433a0 100644 --- a/src/tokens.js +++ b/src/tokens.js @@ -155,6 +155,7 @@ const TokenNames = { T_NAME_RELATIVE: 241, T_NAME_QUALIFIED: 242, T_NAME_FULLY_QUALIFIED: 243, + T_PIPE: 244, }; /** diff --git a/test/snapshot/__snapshots__/pipe.test.js.snap b/test/snapshot/__snapshots__/pipe.test.js.snap new file mode 100644 index 00000000..82901ebb --- /dev/null +++ b/test/snapshot/__snapshots__/pipe.test.js.snap @@ -0,0 +1,106 @@ +// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing + +exports[`Parse Pipeline Operator can parse basic pipeline operator task 1`] = ` +Program { + "children": [ + ExpressionStatement { + "expression": Assign { + "kind": "assign", + "left": Variable { + "curly": false, + "kind": "variable", + "name": "c", + }, + "operator": "=", + "right": Bin { + "kind": "bin", + "left": Variable { + "curly": false, + "kind": "variable", + "name": "a", + }, + "right": Closure { + "arguments": [ + Parameter { + "attrGroups": [], + "byref": false, + "flags": 0, + "kind": "parameter", + "name": Identifier { + "kind": "identifier", + "name": "s", + }, + "nullable": false, + "readonly": false, + "type": null, + "value": null, + "variadic": false, + }, + ], + "attrGroups": [], + "body": Bin { + "kind": "bin", + "left": Call { + "arguments": [ + String { + "isDoubleQuote": true, + "kind": "string", + "raw": "","", + "unicode": false, + "value": ",", + }, + Variable { + "curly": false, + "kind": "variable", + "name": "s", + }, + ], + "kind": "call", + "what": Name { + "kind": "name", + "name": "explode", + "resolution": "uqn", + }, + }, + "right": Bin { + "kind": "bin", + "left": Call { + "arguments": [ + VariadicPlaceholder { + "kind": "variadicplaceholder", + }, + ], + "kind": "call", + "what": Name { + "kind": "name", + "name": "array_filter", + "resolution": "uqn", + }, + }, + "right": String { + "isDoubleQuote": false, + "kind": "string", + "raw": "'array_last'", + "unicode": false, + "value": "array_last", + }, + "type": "|>", + }, + "type": "|>", + }, + "byref": false, + "isStatic": false, + "kind": "arrowfunc", + "nullable": false, + "type": null, + }, + "type": "|>", + }, + }, + "kind": "expressionstatement", + }, + ], + "errors": [], + "kind": "program", +} +`; diff --git a/test/snapshot/pipe.test.js b/test/snapshot/pipe.test.js new file mode 100644 index 00000000..f65b98ec --- /dev/null +++ b/test/snapshot/pipe.test.js @@ -0,0 +1,18 @@ +const parser = require("../main"); + +describe("Parse Pipeline Operator", () => { + it("can parse basic pipeline operator task", () => { + const parser8_5 = parser.create({ + parser: { + version: "8.5", + }, + }); + expect( + parser8_5.parseEval(` + $c = $a + |> fn ($s) => explode(",", $s) + |> array_filter(...) |> 'array_last' ; + `), + ).toMatchSnapshot(); + }); +}); diff --git a/types.d.ts b/types.d.ts index a5ad9c93..be9cea2c 100644 --- a/types.d.ts +++ b/types.d.ts @@ -1434,6 +1434,7 @@ declare module "php-parser" { T_NAME_RELATIVE = 241, T_NAME_QUALIFIED = 242, T_NAME_FULLY_QUALIFIED = 243, + T_PIPE = 244, } /** * PHP AST Tokens