diff --git a/src/parser/utils.js b/src/parser/utils.js index ae13d2c63..bef0b2537 100644 --- a/src/parser/utils.js +++ b/src/parser/utils.js @@ -116,7 +116,7 @@ module.exports = { const result = cb(); if (result) { this.ast.swapLocations(result, byref, result, this); - result.byref = true; + result.byref = true; } return result; }, diff --git a/src/parser/variable.js b/src/parser/variable.js index 50a00dd14..4c58c9b93 100644 --- a/src/parser/variable.js +++ b/src/parser/variable.js @@ -126,33 +126,6 @@ module.exports = { if (is_static_lookup && this.token === this.tok.T_OBJECT_OPERATOR) { this.error(); } - - if (this.token === this.tok.T_VARIABLE) { - const inner = this.node("variable"); - name = this.text().substring(1); - this.next(); - what = this.node("encapsed")( - [what, inner(name, false)], - null, - "offset" - ); - if (what.loc && what.value[0].loc) { - what.loc.start = what.value[0].loc.start; - } - } else if (this.token === "{") { - // EncapsedPart - const part = this.node("encapsedpart"); - const expr = this.next().read_expr(); - this.expect("}") && this.next(); - what = this.node("encapsed")( - [what, part(expr, true)], - null, - "offset" - ); - if (what.loc && what.value[0].loc) { - what.loc.start = what.value[0].loc.start; - } - } break; case this.tok.T_VARIABLE: what = this.node("variable"); @@ -209,23 +182,30 @@ module.exports = { } break; case "[": + case "{": { + const backet = this.token; + const isSquareBracket = backet === "["; node = this.node("offsetlookup"); this.next(); offset = false; if (encapsed) { offset = this.read_encaps_var_offset(); - this.expect("]") && this.next(); + this.expect(isSquareBracket ? "]" : "}") && this.next(); } else { + const isCallableVariable = isSquareBracket + ? this.token !== "]" + : this.token !== "}"; // callable_variable : https://github.com/php/php-src/blob/493524454d66adde84e00d249d607ecd540de99f/Zend/zend_language_parser.y#L1122 - if (this.token !== "]") { + if (isCallableVariable) { offset = this.read_expr(); - this.expect("]") && this.next(); + this.expect(isSquareBracket ? "]" : "}") && this.next(); } else { this.next(); } } result = node(result, offset); break; + } case this.tok.T_DOUBLE_COLON: // @see https://github.com/glayzzle/php-parser/issues/107#issuecomment-354104574 if ( diff --git a/test/snapshot/__snapshots__/break.test.js.snap b/test/snapshot/__snapshots__/break.test.js.snap index c17f2cdb1..a11d5505b 100644 --- a/test/snapshot/__snapshots__/break.test.js.snap +++ b/test/snapshot/__snapshots__/break.test.js.snap @@ -61,15 +61,15 @@ Program { } `; -exports[`break with parens 1`] = ` +exports[`break with expression 1`] = ` Program { "children": Array [ Break { "kind": "break", - "level": Number { - "kind": "number", - "parenthesizedExpression": true, - "value": "1", + "level": Variable { + "curly": false, + "kind": "variable", + "name": "var", }, }, ], @@ -78,15 +78,15 @@ Program { } `; -exports[`break with var 1`] = ` +exports[`break with parens 1`] = ` Program { "children": Array [ Break { "kind": "break", - "level": Variable { - "curly": false, - "kind": "variable", - "name": "var", + "level": Number { + "kind": "number", + "parenthesizedExpression": true, + "value": "1", }, }, ], diff --git a/test/snapshot/__snapshots__/expr.test.js.snap b/test/snapshot/__snapshots__/expr.test.js.snap index 0a63b2c2a..b789861b4 100644 --- a/test/snapshot/__snapshots__/expr.test.js.snap +++ b/test/snapshot/__snapshots__/expr.test.js.snap @@ -114,44 +114,37 @@ Program { "kind": "identifier", "name": "foo", }, - "what": PropertyLookup { - "kind": "propertylookup", - "offset": Encapsed { - "kind": "encapsed", - "type": "offset", - "value": Array [ - Identifier { - "kind": "identifier", - "name": "bar", - }, - EncapsedPart { - "curly": true, - "expression": Variable { + "what": OffsetLookup { + "kind": "offsetlookup", + "offset": Variable { + "curly": false, + "kind": "variable", + "name": "baz", + }, + "what": PropertyLookup { + "kind": "propertylookup", + "offset": Identifier { + "kind": "identifier", + "name": "bar", + }, + "what": Call { + "arguments": Array [ + Variable { "curly": false, "kind": "variable", - "name": "baz", + "name": "foo", + }, + ], + "kind": "call", + "what": Post { + "kind": "post", + "parenthesizedExpression": true, + "type": "+", + "what": Variable { + "curly": false, + "kind": "variable", + "name": "a", }, - "kind": "encapsedpart", - }, - ], - }, - "what": Call { - "arguments": Array [ - Variable { - "curly": false, - "kind": "variable", - "name": "foo", - }, - ], - "kind": "call", - "what": Post { - "kind": "post", - "parenthesizedExpression": true, - "type": "+", - "what": Variable { - "curly": false, - "kind": "variable", - "name": "a", }, }, }, diff --git a/test/snapshot/__snapshots__/graceful.test.js.snap b/test/snapshot/__snapshots__/graceful.test.js.snap index 6a4368b19..09c466d1a 100644 --- a/test/snapshot/__snapshots__/graceful.test.js.snap +++ b/test/snapshot/__snapshots__/graceful.test.js.snap @@ -145,6 +145,108 @@ Program { } `; +exports[`Test graceful mode to suppress errors should fail with '[' and '}' 1`] = ` +Program { + "children": Array [ + ExpressionStatement { + "expression": OffsetLookup { + "kind": "offsetlookup", + "offset": Variable { + "curly": false, + "kind": "variable", + "name": "bar", + }, + "what": Variable { + "curly": false, + "kind": "variable", + "name": "obj", + }, + }, + "kind": "expressionstatement", + }, + ExpressionStatement { + "expression": undefined, + "kind": "expressionstatement", + }, + ], + "errors": Array [ + Error { + "expected": "]", + "kind": "error", + "line": 1, + "message": "Parse Error : syntax error, unexpected '}', expecting ']' on line 1", + "token": "'}'", + }, + Error { + "expected": ";", + "kind": "error", + "line": 1, + "message": "Parse Error : syntax error, unexpected '}', expecting ';' on line 1", + "token": "'}'", + }, + Error { + "expected": "EXPR", + "kind": "error", + "line": 1, + "message": "Parse Error : syntax error, unexpected '}' on line 1", + "token": "'}'", + }, + ], + "kind": "program", +} +`; + +exports[`Test graceful mode to suppress errors should fail with '{' and ']' 1`] = ` +Program { + "children": Array [ + ExpressionStatement { + "expression": OffsetLookup { + "kind": "offsetlookup", + "offset": Variable { + "curly": false, + "kind": "variable", + "name": "foo", + }, + "what": Variable { + "curly": false, + "kind": "variable", + "name": "obj", + }, + }, + "kind": "expressionstatement", + }, + ExpressionStatement { + "expression": undefined, + "kind": "expressionstatement", + }, + ], + "errors": Array [ + Error { + "expected": "}", + "kind": "error", + "line": 1, + "message": "Parse Error : syntax error, unexpected ']', expecting '}' on line 1", + "token": "']'", + }, + Error { + "expected": ";", + "kind": "error", + "line": 1, + "message": "Parse Error : syntax error, unexpected ']', expecting ';' on line 1", + "token": "']'", + }, + Error { + "expected": "EXPR", + "kind": "error", + "line": 1, + "message": "Parse Error : syntax error, unexpected ']' on line 1", + "token": "']'", + }, + ], + "kind": "program", +} +`; + exports[`Test graceful mode to suppress errors staticlookup 1`] = ` Program { "children": Array [ diff --git a/test/snapshot/__snapshots__/offsetlookup.test.js.snap b/test/snapshot/__snapshots__/offsetlookup.test.js.snap index 22da6af60..1f43f2c57 100644 --- a/test/snapshot/__snapshots__/offsetlookup.test.js.snap +++ b/test/snapshot/__snapshots__/offsetlookup.test.js.snap @@ -1,5 +1,34 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP +exports[`offsetlookup call (curly) 1`] = ` +Program { + "children": Array [ + ExpressionStatement { + "expression": Call { + "arguments": Array [], + "kind": "call", + "what": OffsetLookup { + "kind": "offsetlookup", + "offset": Variable { + "curly": false, + "kind": "variable", + "name": "var", + }, + "what": Variable { + "curly": false, + "kind": "variable", + "name": "obj", + }, + }, + }, + "kind": "expressionstatement", + }, + ], + "errors": Array [], + "kind": "program", +} +`; + exports[`offsetlookup call 1`] = ` Program { "children": Array [ @@ -29,6 +58,341 @@ Program { } `; +exports[`offsetlookup inside propertylookup (curly) 1`] = ` +Program { + "children": Array [ + ExpressionStatement { + "expression": OffsetLookup { + "kind": "offsetlookup", + "offset": Number { + "kind": "number", + "value": "1", + }, + "what": PropertyLookup { + "kind": "propertylookup", + "offset": Identifier { + "kind": "identifier", + "name": "bzr_", + }, + "what": Variable { + "curly": false, + "kind": "variable", + "name": "foo", + }, + }, + }, + "kind": "expressionstatement", + }, + ExpressionStatement { + "expression": OffsetLookup { + "kind": "offsetlookup", + "offset": String { + "isDoubleQuote": false, + "kind": "string", + "raw": "'string'", + "unicode": false, + "value": "string", + }, + "what": PropertyLookup { + "kind": "propertylookup", + "offset": Identifier { + "kind": "identifier", + "name": "bzr_", + }, + "what": Variable { + "curly": false, + "kind": "variable", + "name": "foo", + }, + }, + }, + "kind": "expressionstatement", + }, + ExpressionStatement { + "expression": OffsetLookup { + "kind": "offsetlookup", + "offset": Variable { + "curly": false, + "kind": "variable", + "name": "baz", + }, + "what": PropertyLookup { + "kind": "propertylookup", + "offset": Identifier { + "kind": "identifier", + "name": "bzr_", + }, + "what": Variable { + "curly": false, + "kind": "variable", + "name": "foo", + }, + }, + }, + "kind": "expressionstatement", + }, + ExpressionStatement { + "expression": OffsetLookup { + "kind": "offsetlookup", + "offset": PropertyLookup { + "kind": "propertylookup", + "offset": Identifier { + "kind": "identifier", + "name": "foo", + }, + "what": Variable { + "curly": false, + "kind": "variable", + "name": "baz", + }, + }, + "what": PropertyLookup { + "kind": "propertylookup", + "offset": Identifier { + "kind": "identifier", + "name": "bzr_", + }, + "what": Variable { + "curly": false, + "kind": "variable", + "name": "foo", + }, + }, + }, + "kind": "expressionstatement", + }, + ExpressionStatement { + "expression": OffsetLookup { + "kind": "offsetlookup", + "offset": RetIf { + "falseExpr": String { + "isDoubleQuote": false, + "kind": "string", + "raw": "'two'", + "unicode": false, + "value": "two", + }, + "kind": "retif", + "test": Variable { + "curly": false, + "kind": "variable", + "name": "var", + }, + "trueExpr": String { + "isDoubleQuote": false, + "kind": "string", + "raw": "'one'", + "unicode": false, + "value": "one", + }, + }, + "what": PropertyLookup { + "kind": "propertylookup", + "offset": Identifier { + "kind": "identifier", + "name": "bzr_", + }, + "what": Variable { + "curly": false, + "kind": "variable", + "name": "foo", + }, + }, + }, + "kind": "expressionstatement", + }, + ], + "errors": Array [], + "kind": "program", +} +`; + +exports[`offsetlookup inside propertylookup 1`] = ` +Program { + "children": Array [ + ExpressionStatement { + "expression": OffsetLookup { + "kind": "offsetlookup", + "offset": Number { + "kind": "number", + "value": "1", + }, + "what": PropertyLookup { + "kind": "propertylookup", + "offset": Identifier { + "kind": "identifier", + "name": "bzr_", + }, + "what": Variable { + "curly": false, + "kind": "variable", + "name": "foo", + }, + }, + }, + "kind": "expressionstatement", + }, + ExpressionStatement { + "expression": OffsetLookup { + "kind": "offsetlookup", + "offset": String { + "isDoubleQuote": false, + "kind": "string", + "raw": "'string'", + "unicode": false, + "value": "string", + }, + "what": PropertyLookup { + "kind": "propertylookup", + "offset": Identifier { + "kind": "identifier", + "name": "bzr_", + }, + "what": Variable { + "curly": false, + "kind": "variable", + "name": "foo", + }, + }, + }, + "kind": "expressionstatement", + }, + ExpressionStatement { + "expression": OffsetLookup { + "kind": "offsetlookup", + "offset": Variable { + "curly": false, + "kind": "variable", + "name": "baz", + }, + "what": PropertyLookup { + "kind": "propertylookup", + "offset": Identifier { + "kind": "identifier", + "name": "bzr_", + }, + "what": Variable { + "curly": false, + "kind": "variable", + "name": "foo", + }, + }, + }, + "kind": "expressionstatement", + }, + ExpressionStatement { + "expression": OffsetLookup { + "kind": "offsetlookup", + "offset": PropertyLookup { + "kind": "propertylookup", + "offset": Identifier { + "kind": "identifier", + "name": "foo", + }, + "what": Variable { + "curly": false, + "kind": "variable", + "name": "baz", + }, + }, + "what": PropertyLookup { + "kind": "propertylookup", + "offset": Identifier { + "kind": "identifier", + "name": "bzr_", + }, + "what": Variable { + "curly": false, + "kind": "variable", + "name": "foo", + }, + }, + }, + "kind": "expressionstatement", + }, + ExpressionStatement { + "expression": OffsetLookup { + "kind": "offsetlookup", + "offset": RetIf { + "falseExpr": String { + "isDoubleQuote": false, + "kind": "string", + "raw": "'two'", + "unicode": false, + "value": "two", + }, + "kind": "retif", + "test": Variable { + "curly": false, + "kind": "variable", + "name": "var", + }, + "trueExpr": String { + "isDoubleQuote": false, + "kind": "string", + "raw": "'one'", + "unicode": false, + "value": "one", + }, + }, + "what": PropertyLookup { + "kind": "propertylookup", + "offset": Identifier { + "kind": "identifier", + "name": "bzr_", + }, + "what": Variable { + "curly": false, + "kind": "variable", + "name": "foo", + }, + }, + }, + "kind": "expressionstatement", + }, + ], + "errors": Array [], + "kind": "program", +} +`; + +exports[`offsetlookup multiple (curly) 1`] = ` +Program { + "children": Array [ + ExpressionStatement { + "expression": OffsetLookup { + "kind": "offsetlookup", + "offset": String { + "isDoubleQuote": true, + "kind": "string", + "raw": "\\"second\\"", + "unicode": false, + "value": "second", + }, + "what": OffsetLookup { + "kind": "offsetlookup", + "offset": String { + "isDoubleQuote": true, + "kind": "string", + "raw": "\\"first\\"", + "unicode": false, + "value": "first", + }, + "what": Variable { + "curly": false, + "kind": "variable", + "name": "obj", + }, + }, + }, + "kind": "expressionstatement", + }, + ], + "errors": Array [], + "kind": "program", +} +`; + exports[`offsetlookup multiple 1`] = ` Program { "children": Array [ @@ -66,6 +430,33 @@ Program { } `; +exports[`offsetlookup simple (curly) 1`] = ` +Program { + "children": Array [ + ExpressionStatement { + "expression": OffsetLookup { + "kind": "offsetlookup", + "offset": String { + "isDoubleQuote": true, + "kind": "string", + "raw": "\\"index\\"", + "unicode": false, + "value": "index", + }, + "what": Variable { + "curly": false, + "kind": "variable", + "name": "obj", + }, + }, + "kind": "expressionstatement", + }, + ], + "errors": Array [], + "kind": "program", +} +`; + exports[`offsetlookup simple 1`] = ` Program { "children": Array [ @@ -93,6 +484,31 @@ Program { } `; +exports[`offsetlookup variable (curly) 1`] = ` +Program { + "children": Array [ + ExpressionStatement { + "expression": OffsetLookup { + "kind": "offsetlookup", + "offset": Variable { + "curly": false, + "kind": "variable", + "name": "var", + }, + "what": Variable { + "curly": false, + "kind": "variable", + "name": "obj", + }, + }, + "kind": "expressionstatement", + }, + ], + "errors": Array [], + "kind": "program", +} +`; + exports[`offsetlookup variable 1`] = ` Program { "children": Array [ diff --git a/test/snapshot/__snapshots__/variable.test.js.snap b/test/snapshot/__snapshots__/variable.test.js.snap index d0c487642..6d2aa7e3b 100644 --- a/test/snapshot/__snapshots__/variable.test.js.snap +++ b/test/snapshot/__snapshots__/variable.test.js.snap @@ -1280,31 +1280,24 @@ Program { "kind": "expressionstatement", }, ExpressionStatement { - "expression": PropertyLookup { - "kind": "propertylookup", - "offset": Encapsed { - "kind": "encapsed", - "type": "offset", - "value": Array [ - Identifier { - "kind": "identifier", - "name": "foo_", - }, - EncapsedPart { - "curly": true, - "expression": Variable { - "curly": false, - "kind": "variable", - "name": "property", - }, - "kind": "encapsedpart", - }, - ], - }, - "what": Variable { + "expression": OffsetLookup { + "kind": "offsetlookup", + "offset": Variable { "curly": false, "kind": "variable", - "name": "bar", + "name": "property", + }, + "what": PropertyLookup { + "kind": "propertylookup", + "offset": Identifier { + "kind": "identifier", + "name": "foo_", + }, + "what": Variable { + "curly": false, + "kind": "variable", + "name": "bar", + }, }, }, "kind": "expressionstatement", diff --git a/test/snapshot/graceful.test.js b/test/snapshot/graceful.test.js index c8344f8c5..51708d441 100644 --- a/test/snapshot/graceful.test.js +++ b/test/snapshot/graceful.test.js @@ -94,5 +94,13 @@ describe("Test graceful mode", function() { it("should fail !", function() { expect(test.parseEval("new Foo::{call()}();")).toMatchSnapshot(); }); + + it("should fail with '{' and ']'", function() { + expect(test.parseEval("$obj{$foo];")).toMatchSnapshot(); + }); + + it("should fail with '[' and '}'", function() { + expect(test.parseEval("$obj[$bar};")).toMatchSnapshot(); + }); }); }); diff --git a/test/snapshot/offsetlookup.test.js b/test/snapshot/offsetlookup.test.js index 490619907..90d7f33be 100644 --- a/test/snapshot/offsetlookup.test.js +++ b/test/snapshot/offsetlookup.test.js @@ -13,4 +13,34 @@ describe("offsetlookup", function() { it("multiple", function() { expect(parser.parseEval('$obj["first"]["second"];')).toMatchSnapshot(); }); + it("simple (curly)", function() { + expect(parser.parseEval('$obj{"index"};')).toMatchSnapshot(); + }); + it("variable (curly)", function() { + expect(parser.parseEval('$obj{$var};')).toMatchSnapshot(); + }); + it("call (curly)", function() { + expect(parser.parseEval('$obj{$var}();')).toMatchSnapshot(); + }); + it("multiple (curly)", function() { + expect(parser.parseEval('$obj{"first"}{"second"};')).toMatchSnapshot(); + }); + it("inside propertylookup", function() { + expect(parser.parseEval(` +$foo->bzr_[1]; +$foo->bzr_['string']; +$foo->bzr_[$baz]; +$foo->bzr_[$baz->foo]; +$foo->bzr_[$var ? 'one' : 'two']; + `)).toMatchSnapshot(); + }); + it("inside propertylookup (curly)", function() { + expect(parser.parseEval(` +$foo->bzr_{1}; +$foo->bzr_{'string'}; +$foo->bzr_{$baz}; +$foo->bzr_{$baz->foo}; +$foo->bzr_{$var ? 'one' : 'two'}; + `)).toMatchSnapshot(); + }); });