From 319e03f7e78bfb8b8cc0967d6af8a2287bcc38a1 Mon Sep 17 00:00:00 2001 From: Christian Zosel Date: Tue, 23 Feb 2021 22:21:45 +0100 Subject: [PATCH 1/3] update node version in CI --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 3aec2b7f8..0b0cfa25e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,6 +1,6 @@ language: node_js node_js: - - '6' + - '10' cache: bundler: true directories: From 8b77c609c88ac2698a190f1f9f4ca8f034c1d175 Mon Sep 17 00:00:00 2001 From: Leo Date: Mon, 15 Feb 2021 09:34:11 +0100 Subject: [PATCH 2/3] add php8 nullsafe operator --- src/ast.js | 1 + src/ast/nullsafepropertylookup.js | 23 +++++++++++++++++++++++ src/index.js | 4 ++-- src/lexer/tokens.js | 8 ++++++++ src/parser/variable.js | 5 +++++ src/tokens.js | 2 ++ test/version.test.js | 6 +++--- 7 files changed, 44 insertions(+), 5 deletions(-) create mode 100644 src/ast/nullsafepropertylookup.js diff --git a/src/ast.js b/src/ast.js index bff1c2959..67f9644d5 100644 --- a/src/ast.js +++ b/src/ast.js @@ -522,6 +522,7 @@ AST.prototype.checkNodes = function () { require("./ast/noop"), require("./ast/nowdoc"), require("./ast/nullkeyword"), + require("./ast/nullsafepropertylookup"), require("./ast/number"), require("./ast/offsetlookup"), require("./ast/operation"), diff --git a/src/ast/nullsafepropertylookup.js b/src/ast/nullsafepropertylookup.js new file mode 100644 index 000000000..87e6bfde0 --- /dev/null +++ b/src/ast/nullsafepropertylookup.js @@ -0,0 +1,23 @@ +/** + * Copyright (C) 2018 Glayzzle (BSD3 License) + * @authors https://github.com/glayzzle/php-parser/graphs/contributors + * @url http://glayzzle.com + */ +"use strict"; + +const Lookup = require("./lookup"); +const KIND = "nullsafepropertylookup"; + +/** + * Lookup to an object property + * @constructor PropertyLookup + * @extends {Lookup} + */ +module.exports = Lookup.extends(KIND, function PropertyLookup( + what, + offset, + docs, + location +) { + Lookup.apply(this, [KIND, what, offset, docs, location]); +}); diff --git a/src/index.js b/src/index.js index 24bd5c874..fd74893e1 100644 --- a/src/index.js +++ b/src/index.js @@ -92,8 +92,8 @@ const engine = function (options) { } else if (typeof options.parser.version !== "number") { throw new Error("Expecting a number for version"); } - if (options.parser.version < 500 || options.parser.version > 704) { - throw new Error("Can only handle versions between 5.x to 7.x"); + if (options.parser.version < 500 || options.parser.version > 900) { + throw new Error("Can only handle versions between 5.x to 8.x"); } } } diff --git a/src/lexer/tokens.js b/src/lexer/tokens.js index 092c0aa51..eedcea35a 100644 --- a/src/lexer/tokens.js +++ b/src/lexer/tokens.js @@ -162,6 +162,14 @@ module.exports = { return this.tok.T_COALESCE; } } + if ( + this.version >= 800 && + this._input[this.offset] === "-" && + this._input[this.offset + 1] === ">" + ) { + this.consume(2); + return this.tok.T_NULLSAFE_OBJECT_OPERATOR; + } return "?"; }, "<": function () { diff --git a/src/parser/variable.js b/src/parser/variable.js index 1f3d17e23..bf4bd7470 100644 --- a/src/parser/variable.js +++ b/src/parser/variable.js @@ -228,6 +228,11 @@ module.exports = { result = node(result, this.read_what()); break; } + case this.tok.T_NULLSAFE_OBJECT_OPERATOR: { + node = this.node("nullsafepropertylookup"); + result = node(result, this.read_what()); + break; + } default: break recursive_scan_loop; } diff --git a/src/tokens.js b/src/tokens.js index 1e1af82c4..e2c9574c3 100644 --- a/src/tokens.js +++ b/src/tokens.js @@ -146,6 +146,7 @@ module.exports = { 233: "T_SPACESHIP", 234: "T_COALESCE_EQUAL", 235: "T_FN", + 236: "T_NULLSAFE_OBJECT_OPERATOR", }, names: { T_HALT_COMPILER: 101, @@ -283,5 +284,6 @@ module.exports = { T_SPACESHIP: 233, T_COALESCE_EQUAL: 234, T_FN: 235, + T_NULLSAFE_OBJECT_OPERATOR: 236, }, }; diff --git a/test/version.test.js b/test/version.test.js index cff7fb860..e678a82bc 100644 --- a/test/version.test.js +++ b/test/version.test.js @@ -42,13 +42,13 @@ describe("Test versions", function () { version: "4.9", }, }) - ).toThrow(new Error("Can only handle versions between 5.x to 7.x")); + ).toThrow(new Error("Can only handle versions between 5.x to 8.x")); expect( parser.create.bind(null, { parser: { - version: "8.9", + version: "9.9", }, }) - ).toThrow(new Error("Can only handle versions between 5.x to 7.x")); + ).toThrow(new Error("Can only handle versions between 5.x to 8.x")); }); }); From 4ca49ba193317f93273b221e74ff96ea4ac50ace Mon Sep 17 00:00:00 2001 From: Leo Date: Tue, 23 Feb 2021 09:21:06 +0100 Subject: [PATCH 3/3] add tests --- src/lexer.js | 2 +- src/parser.js | 2 +- test/snapshot/__snapshots__/call.test.js.snap | 63 ++ .../__snapshots__/encapsed.test.js.snap | 740 +++++++++++++++--- .../nullsavepropertylookup.test.js.snap | 109 +++ test/snapshot/call.test.js | 12 + test/snapshot/encapsed.test.js | 33 +- test/snapshot/nullsavepropertylookup.test.js | 18 + 8 files changed, 860 insertions(+), 119 deletions(-) create mode 100644 test/snapshot/__snapshots__/nullsavepropertylookup.test.js.snap create mode 100644 test/snapshot/nullsavepropertylookup.test.js diff --git a/src/lexer.js b/src/lexer.js index 008c0dc1e..facc74b0d 100644 --- a/src/lexer.js +++ b/src/lexer.js @@ -29,7 +29,7 @@ const lexer = function (engine) { this.mode_eval = false; this.asp_tags = false; this.short_tags = false; - this.version = 704; + this.version = 800; this.yyprevcol = 0; this.keywords = { __class__: this.tok.T_CLASS_C, diff --git a/src/parser.js b/src/parser.js index 3d4cab0b4..1d7ee86d4 100644 --- a/src/parser.js +++ b/src/parser.js @@ -33,7 +33,7 @@ const parser = function (lexer, ast) { this.token = null; this.prev = null; this.debug = false; - this.version = 704; + this.version = 800; this.extractDoc = false; this.extractTokens = false; this.suppressErrors = false; diff --git a/test/snapshot/__snapshots__/call.test.js.snap b/test/snapshot/__snapshots__/call.test.js.snap index 4f519057e..b3ffe948a 100644 --- a/test/snapshot/__snapshots__/call.test.js.snap +++ b/test/snapshot/__snapshots__/call.test.js.snap @@ -793,6 +793,69 @@ Program { } `; +exports[`Test call nullsafepropertylookup (2) 1`] = ` +Program { + "children": Array [ + ExpressionStatement { + "expression": Call { + "arguments": Array [], + "kind": "call", + "what": PropertyLookup { + "kind": "nullsafepropertylookup", + "offset": Identifier { + "kind": "identifier", + "name": "call", + }, + "what": PropertyLookup { + "kind": "nullsafepropertylookup", + "offset": Identifier { + "kind": "identifier", + "name": "property", + }, + "what": Variable { + "curly": false, + "kind": "variable", + "name": "obj", + }, + }, + }, + }, + "kind": "expressionstatement", + }, + ], + "errors": Array [], + "kind": "program", +} +`; + +exports[`Test call nullsafepropertylookup 1`] = ` +Program { + "children": Array [ + ExpressionStatement { + "expression": Call { + "arguments": Array [], + "kind": "call", + "what": PropertyLookup { + "kind": "nullsafepropertylookup", + "offset": Identifier { + "kind": "identifier", + "name": "call", + }, + "what": Variable { + "curly": false, + "kind": "variable", + "name": "obj", + }, + }, + }, + "kind": "expressionstatement", + }, + ], + "errors": Array [], + "kind": "program", +} +`; + exports[`Test call offsetlookup 1`] = ` Program { "children": Array [ diff --git a/test/snapshot/__snapshots__/encapsed.test.js.snap b/test/snapshot/__snapshots__/encapsed.test.js.snap index cc5701cfb..cedaaa637 100644 --- a/test/snapshot/__snapshots__/encapsed.test.js.snap +++ b/test/snapshot/__snapshots__/encapsed.test.js.snap @@ -645,13 +645,531 @@ Program { } `; +exports[`encapsed multiple nullsafepropertylookup (complex syntax) 1`] = ` +Program { + "children": Array [ + ExpressionStatement { + "expression": Encapsed { + "kind": "encapsed", + "raw": "\\"string {$obj?->property?->property} string\\"", + "type": "string", + "value": Array [ + EncapsedPart { + "curly": false, + "expression": String { + "isDoubleQuote": false, + "kind": "string", + "raw": "string ", + "unicode": false, + "value": "string ", + }, + "kind": "encapsedpart", + "syntax": null, + }, + EncapsedPart { + "curly": false, + "expression": PropertyLookup { + "kind": "nullsafepropertylookup", + "offset": Identifier { + "kind": "identifier", + "name": "property", + }, + "what": PropertyLookup { + "kind": "nullsafepropertylookup", + "offset": Identifier { + "kind": "identifier", + "name": "property", + }, + "what": Variable { + "curly": false, + "kind": "variable", + "name": "obj", + }, + }, + }, + "kind": "encapsedpart", + "syntax": "complex", + }, + EncapsedPart { + "curly": false, + "expression": String { + "isDoubleQuote": false, + "kind": "string", + "raw": " string", + "unicode": false, + "value": " string", + }, + "kind": "encapsedpart", + "syntax": null, + }, + ], + }, + "kind": "expressionstatement", + }, + ], + "errors": Array [], + "kind": "program", +} +`; + exports[`encapsed multiple propertylookup (complex syntax) 1`] = ` Program { "children": Array [ ExpressionStatement { "expression": Encapsed { "kind": "encapsed", - "raw": "\\"string {$obj->property->property} string\\"", + "raw": "\\"string {$obj->property->property} string\\"", + "type": "string", + "value": Array [ + EncapsedPart { + "curly": false, + "expression": String { + "isDoubleQuote": false, + "kind": "string", + "raw": "string ", + "unicode": false, + "value": "string ", + }, + "kind": "encapsedpart", + "syntax": null, + }, + EncapsedPart { + "curly": false, + "expression": PropertyLookup { + "kind": "propertylookup", + "offset": Identifier { + "kind": "identifier", + "name": "property", + }, + "what": PropertyLookup { + "kind": "propertylookup", + "offset": Identifier { + "kind": "identifier", + "name": "property", + }, + "what": Variable { + "curly": false, + "kind": "variable", + "name": "obj", + }, + }, + }, + "kind": "encapsedpart", + "syntax": "complex", + }, + EncapsedPart { + "curly": false, + "expression": String { + "isDoubleQuote": false, + "kind": "string", + "raw": " string", + "unicode": false, + "value": " string", + }, + "kind": "encapsedpart", + "syntax": null, + }, + ], + }, + "kind": "expressionstatement", + }, + ], + "errors": Array [], + "kind": "program", +} +`; + +exports[`encapsed negative offset in encapsed var offset 1`] = ` +Program { + "children": Array [ + ExpressionStatement { + "expression": Encapsed { + "kind": "encapsed", + "raw": "\\"$var[-1]\\"", + "type": "string", + "value": Array [ + EncapsedPart { + "curly": false, + "expression": OffsetLookup { + "kind": "offsetlookup", + "offset": Number { + "kind": "number", + "value": -1, + }, + "what": Variable { + "curly": false, + "kind": "variable", + "name": "var", + }, + }, + "kind": "encapsedpart", + "syntax": "simple", + }, + ], + }, + "kind": "expressionstatement", + }, + ], + "errors": Array [], + "kind": "program", +} +`; + +exports[`encapsed newline before closing curly (complex syntax) 1`] = ` +Program { + "children": Array [ + ExpressionStatement { + "expression": Encapsed { + "kind": "encapsed", + "raw": "\\"string {$var +} string\\"", + "type": "string", + "value": Array [ + EncapsedPart { + "curly": false, + "expression": String { + "isDoubleQuote": false, + "kind": "string", + "raw": "string ", + "unicode": false, + "value": "string ", + }, + "kind": "encapsedpart", + "syntax": null, + }, + EncapsedPart { + "curly": false, + "expression": Variable { + "curly": false, + "kind": "variable", + "name": "var", + }, + "kind": "encapsedpart", + "syntax": "complex", + }, + EncapsedPart { + "curly": false, + "expression": String { + "isDoubleQuote": false, + "kind": "string", + "raw": " string", + "unicode": false, + "value": " string", + }, + "kind": "encapsedpart", + "syntax": null, + }, + ], + }, + "kind": "expressionstatement", + }, + ], + "errors": Array [], + "kind": "program", +} +`; + +exports[`encapsed no curly 1`] = ` +Program { + "children": Array [ + ExpressionStatement { + "expression": Encapsed { + "kind": "encapsed", + "raw": "\\"string $$juice string\\"", + "type": "string", + "value": Array [ + EncapsedPart { + "curly": false, + "expression": String { + "isDoubleQuote": false, + "kind": "string", + "raw": "string $", + "unicode": false, + "value": "string $", + }, + "kind": "encapsedpart", + "syntax": null, + }, + EncapsedPart { + "curly": false, + "expression": Variable { + "curly": false, + "kind": "variable", + "name": "juice", + }, + "kind": "encapsedpart", + "syntax": "simple", + }, + EncapsedPart { + "curly": false, + "expression": String { + "isDoubleQuote": false, + "kind": "string", + "raw": " string", + "unicode": false, + "value": " string", + }, + "kind": "encapsedpart", + "syntax": null, + }, + ], + }, + "kind": "expressionstatement", + }, + ], + "errors": Array [], + "kind": "program", +} +`; + +exports[`encapsed nullsafepropertylookup (complex syntax) 1`] = ` +Program { + "children": Array [ + ExpressionStatement { + "expression": Encapsed { + "kind": "encapsed", + "raw": "\\"string {$obj?->property} string\\"", + "type": "string", + "value": Array [ + EncapsedPart { + "curly": false, + "expression": String { + "isDoubleQuote": false, + "kind": "string", + "raw": "string ", + "unicode": false, + "value": "string ", + }, + "kind": "encapsedpart", + "syntax": null, + }, + EncapsedPart { + "curly": false, + "expression": PropertyLookup { + "kind": "nullsafepropertylookup", + "offset": Identifier { + "kind": "identifier", + "name": "property", + }, + "what": Variable { + "curly": false, + "kind": "variable", + "name": "obj", + }, + }, + "kind": "encapsedpart", + "syntax": "complex", + }, + EncapsedPart { + "curly": false, + "expression": String { + "isDoubleQuote": false, + "kind": "string", + "raw": " string", + "unicode": false, + "value": " string", + }, + "kind": "encapsedpart", + "syntax": null, + }, + ], + }, + "kind": "expressionstatement", + }, + ], + "errors": Array [], + "kind": "program", +} +`; + +exports[`encapsed nullsafepropertylookup (simple syntax) 1`] = ` +Program { + "children": Array [ + ExpressionStatement { + "expression": Encapsed { + "kind": "encapsed", + "raw": "\\"string $obj?->property string\\"", + "type": "string", + "value": Array [ + EncapsedPart { + "curly": false, + "expression": String { + "isDoubleQuote": false, + "kind": "string", + "raw": "string ", + "unicode": false, + "value": "string ", + }, + "kind": "encapsedpart", + "syntax": null, + }, + EncapsedPart { + "curly": false, + "expression": Variable { + "curly": false, + "kind": "variable", + "name": "obj", + }, + "kind": "encapsedpart", + "syntax": "simple", + }, + EncapsedPart { + "curly": false, + "expression": String { + "isDoubleQuote": false, + "kind": "string", + "raw": "?->property string", + "unicode": false, + "value": "?->property string", + }, + "kind": "encapsedpart", + "syntax": null, + }, + ], + }, + "kind": "expressionstatement", + }, + ], + "errors": Array [], + "kind": "program", +} +`; + +exports[`encapsed nullsafepropertylookup 1`] = ` +Program { + "children": Array [ + ExpressionStatement { + "expression": PropertyLookup { + "kind": "nullsafepropertylookup", + "offset": EncapsedPart { + "curly": false, + "expression": Encapsed { + "kind": "encapsed", + "raw": "\\"set{$type}\\"", + "type": "string", + "value": Array [ + EncapsedPart { + "curly": false, + "expression": String { + "isDoubleQuote": false, + "kind": "string", + "raw": "set", + "unicode": false, + "value": "set", + }, + "kind": "encapsedpart", + "syntax": null, + }, + EncapsedPart { + "curly": false, + "expression": Variable { + "curly": false, + "kind": "variable", + "name": "type", + }, + "kind": "encapsedpart", + "syntax": "complex", + }, + ], + }, + "kind": "encapsedpart", + "syntax": "complex", + }, + "what": Variable { + "curly": false, + "kind": "variable", + "name": "this", + }, + }, + "kind": "expressionstatement", + }, + ], + "errors": Array [], + "kind": "program", +} +`; + +exports[`encapsed nullsafepropertylookup and offsetlookup (complex syntax) 1`] = ` +Program { + "children": Array [ + ExpressionStatement { + "expression": Encapsed { + "kind": "encapsed", + "raw": "\\"string {$obj?->values[3]?->name} string\\"", + "type": "string", + "value": Array [ + EncapsedPart { + "curly": false, + "expression": String { + "isDoubleQuote": false, + "kind": "string", + "raw": "string ", + "unicode": false, + "value": "string ", + }, + "kind": "encapsedpart", + "syntax": null, + }, + EncapsedPart { + "curly": false, + "expression": PropertyLookup { + "kind": "nullsafepropertylookup", + "offset": Identifier { + "kind": "identifier", + "name": "name", + }, + "what": OffsetLookup { + "kind": "offsetlookup", + "offset": Number { + "kind": "number", + "value": "3", + }, + "what": PropertyLookup { + "kind": "nullsafepropertylookup", + "offset": Identifier { + "kind": "identifier", + "name": "values", + }, + "what": Variable { + "curly": false, + "kind": "variable", + "name": "obj", + }, + }, + }, + }, + "kind": "encapsedpart", + "syntax": "complex", + }, + EncapsedPart { + "curly": false, + "expression": String { + "isDoubleQuote": false, + "kind": "string", + "raw": " string", + "unicode": false, + "value": " string", + }, + "kind": "encapsedpart", + "syntax": null, + }, + ], + }, + "kind": "expressionstatement", + }, + ], + "errors": Array [], + "kind": "program", +} +`; + +exports[`encapsed nullsafepropertylookup by variable (2) (complex syntax) 1`] = ` +Program { + "children": Array [ + ExpressionStatement { + "expression": Encapsed { + "kind": "encapsed", + "raw": "\\"string {$obj?->{$array[1]}} string\\"", "type": "string", "value": Array [ EncapsedPart { @@ -669,22 +1187,28 @@ Program { EncapsedPart { "curly": false, "expression": PropertyLookup { - "kind": "propertylookup", - "offset": Identifier { - "kind": "identifier", - "name": "property", - }, - "what": PropertyLookup { - "kind": "propertylookup", - "offset": Identifier { - "kind": "identifier", - "name": "property", - }, - "what": Variable { - "curly": false, - "kind": "variable", - "name": "obj", + "kind": "nullsafepropertylookup", + "offset": EncapsedPart { + "curly": false, + "expression": OffsetLookup { + "kind": "offsetlookup", + "offset": Number { + "kind": "number", + "value": "1", + }, + "what": Variable { + "curly": false, + "kind": "variable", + "name": "array", + }, }, + "kind": "encapsedpart", + "syntax": "complex", + }, + "what": Variable { + "curly": false, + "kind": "variable", + "name": "obj", }, }, "kind": "encapsedpart", @@ -712,31 +1236,56 @@ Program { } `; -exports[`encapsed negative offset in encapsed var offset 1`] = ` +exports[`encapsed nullsafepropertylookup by variable (complex syntax) 1`] = ` Program { "children": Array [ ExpressionStatement { "expression": Encapsed { "kind": "encapsed", - "raw": "\\"$var[-1]\\"", + "raw": "\\"string {$obj?->$var} string\\"", "type": "string", "value": Array [ EncapsedPart { "curly": false, - "expression": OffsetLookup { - "kind": "offsetlookup", - "offset": Number { - "kind": "number", - "value": -1, + "expression": String { + "isDoubleQuote": false, + "kind": "string", + "raw": "string ", + "unicode": false, + "value": "string ", + }, + "kind": "encapsedpart", + "syntax": null, + }, + EncapsedPart { + "curly": false, + "expression": PropertyLookup { + "kind": "nullsafepropertylookup", + "offset": Variable { + "curly": false, + "kind": "variable", + "name": "var", }, "what": Variable { "curly": false, "kind": "variable", - "name": "var", + "name": "obj", }, }, "kind": "encapsedpart", - "syntax": "simple", + "syntax": "complex", + }, + EncapsedPart { + "curly": false, + "expression": String { + "isDoubleQuote": false, + "kind": "string", + "raw": " string", + "unicode": false, + "value": " string", + }, + "kind": "encapsedpart", + "syntax": null, }, ], }, @@ -748,14 +1297,13 @@ Program { } `; -exports[`encapsed newline before closing curly (complex syntax) 1`] = ` +exports[`encapsed nullsafepropertylookup with comments (complex syntax) 1`] = ` Program { "children": Array [ ExpressionStatement { "expression": Encapsed { "kind": "encapsed", - "raw": "\\"string {$var -} string\\"", + "raw": "\\"string {$var?->foo?->bar /* Comment */ } string\\"", "type": "string", "value": Array [ EncapsedPart { @@ -772,10 +1320,24 @@ Program { }, EncapsedPart { "curly": false, - "expression": Variable { - "curly": false, - "kind": "variable", - "name": "var", + "expression": PropertyLookup { + "kind": "nullsafepropertylookup", + "offset": Identifier { + "kind": "identifier", + "name": "bar", + }, + "what": PropertyLookup { + "kind": "nullsafepropertylookup", + "offset": Identifier { + "kind": "identifier", + "name": "foo", + }, + "what": Variable { + "curly": false, + "kind": "variable", + "name": "var", + }, + }, }, "kind": "encapsedpart", "syntax": "complex", @@ -802,13 +1364,13 @@ Program { } `; -exports[`encapsed no curly 1`] = ` +exports[`encapsed nullsafepropertylookup with multiple call (complex syntax) 1`] = ` Program { "children": Array [ ExpressionStatement { "expression": Encapsed { "kind": "encapsed", - "raw": "\\"string $$juice string\\"", + "raw": "\\"string {$obj?->call()?->call()} string\\"", "type": "string", "value": Array [ EncapsedPart { @@ -816,22 +1378,44 @@ Program { "expression": String { "isDoubleQuote": false, "kind": "string", - "raw": "string $", + "raw": "string ", "unicode": false, - "value": "string $", + "value": "string ", }, "kind": "encapsedpart", "syntax": null, }, EncapsedPart { "curly": false, - "expression": Variable { - "curly": false, - "kind": "variable", - "name": "juice", + "expression": Call { + "arguments": Array [], + "kind": "call", + "what": PropertyLookup { + "kind": "nullsafepropertylookup", + "offset": Identifier { + "kind": "identifier", + "name": "call", + }, + "what": Call { + "arguments": Array [], + "kind": "call", + "what": PropertyLookup { + "kind": "nullsafepropertylookup", + "offset": Identifier { + "kind": "identifier", + "name": "call", + }, + "what": Variable { + "curly": false, + "kind": "variable", + "name": "obj", + }, + }, + }, + }, }, "kind": "encapsedpart", - "syntax": "simple", + "syntax": "complex", }, EncapsedPart { "curly": false, @@ -1513,80 +2097,6 @@ Program { } `; -exports[`encapsed propertylookup and offsetlookup (complex syntax) 1`] = ` -Program { - "children": Array [ - ExpressionStatement { - "expression": Encapsed { - "kind": "encapsed", - "raw": "\\"string {$obj->values[3]->name} string\\"", - "type": "string", - "value": Array [ - EncapsedPart { - "curly": false, - "expression": String { - "isDoubleQuote": false, - "kind": "string", - "raw": "string ", - "unicode": false, - "value": "string ", - }, - "kind": "encapsedpart", - "syntax": null, - }, - EncapsedPart { - "curly": false, - "expression": PropertyLookup { - "kind": "propertylookup", - "offset": Identifier { - "kind": "identifier", - "name": "name", - }, - "what": OffsetLookup { - "kind": "offsetlookup", - "offset": Number { - "kind": "number", - "value": "3", - }, - "what": PropertyLookup { - "kind": "propertylookup", - "offset": Identifier { - "kind": "identifier", - "name": "values", - }, - "what": Variable { - "curly": false, - "kind": "variable", - "name": "obj", - }, - }, - }, - }, - "kind": "encapsedpart", - "syntax": "complex", - }, - EncapsedPart { - "curly": false, - "expression": String { - "isDoubleQuote": false, - "kind": "string", - "raw": " string", - "unicode": false, - "value": " string", - }, - "kind": "encapsedpart", - "syntax": null, - }, - ], - }, - "kind": "expressionstatement", - }, - ], - "errors": Array [], - "kind": "program", -} -`; - exports[`encapsed propertylookup by variable (2) (complex syntax) 1`] = ` Program { "children": Array [ diff --git a/test/snapshot/__snapshots__/nullsavepropertylookup.test.js.snap b/test/snapshot/__snapshots__/nullsavepropertylookup.test.js.snap new file mode 100644 index 000000000..ef0cd9c4e --- /dev/null +++ b/test/snapshot/__snapshots__/nullsavepropertylookup.test.js.snap @@ -0,0 +1,109 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`nullsavepropertylookup call 1`] = ` +Program { + "children": Array [ + ExpressionStatement { + "expression": Call { + "arguments": Array [], + "kind": "call", + "what": PropertyLookup { + "kind": "nullsafepropertylookup", + "offset": Identifier { + "kind": "identifier", + "name": "call", + }, + "what": Variable { + "curly": false, + "kind": "variable", + "name": "obj", + }, + }, + }, + "kind": "expressionstatement", + }, + ], + "errors": Array [], + "kind": "program", +} +`; + +exports[`nullsavepropertylookup multiple 1`] = ` +Program { + "children": Array [ + ExpressionStatement { + "expression": PropertyLookup { + "kind": "nullsafepropertylookup", + "offset": Identifier { + "kind": "identifier", + "name": "property_2", + }, + "what": PropertyLookup { + "kind": "nullsafepropertylookup", + "offset": Identifier { + "kind": "identifier", + "name": "property_1", + }, + "what": Variable { + "curly": false, + "kind": "variable", + "name": "obj", + }, + }, + }, + "kind": "expressionstatement", + }, + ], + "errors": Array [], + "kind": "program", +} +`; + +exports[`nullsavepropertylookup simple 1`] = ` +Program { + "children": Array [ + ExpressionStatement { + "expression": PropertyLookup { + "kind": "nullsafepropertylookup", + "offset": Identifier { + "kind": "identifier", + "name": "property", + }, + "what": Variable { + "curly": false, + "kind": "variable", + "name": "obj", + }, + }, + "kind": "expressionstatement", + }, + ], + "errors": Array [], + "kind": "program", +} +`; + +exports[`nullsavepropertylookup variable 1`] = ` +Program { + "children": Array [ + ExpressionStatement { + "expression": PropertyLookup { + "kind": "nullsafepropertylookup", + "offset": Variable { + "curly": false, + "kind": "variable", + "name": "property", + }, + "what": Variable { + "curly": false, + "kind": "variable", + "name": "obj", + }, + }, + "kind": "expressionstatement", + }, + ], + "errors": Array [], + "kind": "program", +} +`; diff --git a/test/snapshot/call.test.js b/test/snapshot/call.test.js index bd500ad45..cd35318d3 100644 --- a/test/snapshot/call.test.js +++ b/test/snapshot/call.test.js @@ -49,6 +49,18 @@ describe("Test call", function () { }); expect(ast).toMatchSnapshot(); }); + it("nullsafepropertylookup", function () { + const ast = parser.parseEval("$obj?->call();", { + parser: { debug: false }, + }); + expect(ast).toMatchSnapshot(); + }); + it("nullsafepropertylookup (2)", function () { + const ast = parser.parseEval("$obj?->property?->call();", { + parser: { debug: false }, + }); + expect(ast).toMatchSnapshot(); + }); it("staticlookup", function () { const ast = parser.parseEval("$obj::call();", { parser: { debug: false }, diff --git a/test/snapshot/encapsed.test.js b/test/snapshot/encapsed.test.js index 4668bdc80..3e95f60e4 100644 --- a/test/snapshot/encapsed.test.js +++ b/test/snapshot/encapsed.test.js @@ -9,17 +9,25 @@ describe("encapsed", function () { ["offsetlookup (2) (simple syntax)", '"string $array[koolaid1] string";'], ["offsetlookup (3) (simple syntax)", '"string $array[0][0] string";'], ["propertylookup (simple syntax)", '"string $obj->property string";'], + [ + "nullsafepropertylookup (simple syntax)", + '"string $obj?->property string";', + ], ["variable with space opening before curly", '"string { $var} string";'], ["variable with before closing curly", '"string {$var } string";'], ["variable (complex syntax)", '"string {$var} string";'], ["propertylookup (complex syntax)", '"string {$obj->property} string";'], + [ + "nullsafepropertylookup (complex syntax)", + '"string {$obj?->property} string";', + ], ["offsetlookup (complex syntax)", '"string {$array["key"]} string";'], ["offsetlookup 2 (complex syntax)", '"string {$array[4][3]} string";'], ["offsetlookup 3 (complex syntax)", '"string {$arr[foo][3]} string";'], ["offsetlookup 4 (complex syntax)", '"string {$arr["foo"][3]} string";'], [ - "propertylookup and offsetlookup (complex syntax)", - '"string {$obj->values[3]->name} string";', + "nullsafepropertylookup and offsetlookup (complex syntax)", + '"string {$obj?->values[3]?->name} string";', ], ["value of the var (complex syntax)", '"string {${$name}} string";'], [ @@ -50,22 +58,42 @@ describe("encapsed", function () { "propertylookup by variable (complex syntax)", '"string {$obj->$var} string";', ], + [ + "nullsafepropertylookup by variable (complex syntax)", + '"string {$obj?->$var} string";', + ], [ "propertylookup by variable (2) (complex syntax)", '"string {$obj->{$array[1]}} string";', ], + [ + "nullsafepropertylookup by variable (2) (complex syntax)", + '"string {$obj?->{$array[1]}} string";', + ], [ "propertylookup with multiple call (complex syntax)", '"string {$obj->call()->call()} string";', ], + [ + "nullsafepropertylookup with multiple call (complex syntax)", + '"string {$obj?->call()?->call()} string";', + ], [ "multiple propertylookup (complex syntax)", '"string {$obj->property->property} string";', ], + [ + "multiple nullsafepropertylookup (complex syntax)", + '"string {$obj?->property?->property} string";', + ], [ "propertylookup with comments (complex syntax)", '"string {$var->foo->bar /* Comment */ } string";', ], + [ + "nullsafepropertylookup with comments (complex syntax)", + '"string {$var?->foo?->bar /* Comment */ } string";', + ], [ "newline before closing curly (complex syntax)", '"string {$var\n} string";', @@ -97,6 +125,7 @@ describe("encapsed", function () { ["curly #3", '"string {$call()} string";'], ["no curly", '"string $$juice string";'], ["propertylookup", '$this->{"set{$type}"};'], + ["nullsafepropertylookup", '$this?->{"set{$type}"};'], ])("%s", function (_, code) { expect(parser.parseEval(code)).toMatchSnapshot(); }); diff --git a/test/snapshot/nullsavepropertylookup.test.js b/test/snapshot/nullsavepropertylookup.test.js new file mode 100644 index 000000000..f8f9bde62 --- /dev/null +++ b/test/snapshot/nullsavepropertylookup.test.js @@ -0,0 +1,18 @@ +const parser = require("../main"); + +describe("nullsavepropertylookup", function () { + it("simple", function () { + expect(parser.parseEval("$obj?->property;")).toMatchSnapshot(); + }); + it("variable", function () { + expect(parser.parseEval("$obj?->$property;")).toMatchSnapshot(); + }); + it("call", function () { + expect(parser.parseEval("$obj?->call();")).toMatchSnapshot(); + }); + it("multiple", function () { + expect( + parser.parseEval("$obj?->property_1?->property_2;") + ).toMatchSnapshot(); + }); +});