diff --git a/CHANGELOG.md b/CHANGELOG.md index e1ed7c3..00e8aa5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. This project tries to adhere to [Semantic Versioning](http://semver.org/). +## Unreleased +### Fixed +- PHP multiline strings (see [#91](https://github.com/Hirse/brackets-outline-list/issues/91)) + +### Changed +- Use php-parser to parse PHP + + ## 1.1.0 - 2017-04-20 ### Added - Arabic translation, thanks to [__@sadik-fattah__](https://github.com/sadik-fattah) diff --git a/README.md b/README.md index e5ce86a..87ea278 100644 --- a/README.md +++ b/README.md @@ -33,7 +33,7 @@ | Pug (Jade) | RegExp | :no_entry_sign: | :x: | :x: | :no_entry_sign: | | JavaScript, JSX | Espree | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | | Markdown, GitHub-Flavored-Markdown | RegExp | :heavy_check_mark: | :no_entry_sign: | :no_entry_sign: | :no_entry_sign: | -| PHP | Lexer | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | +| PHP | php-parser | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | | Python | RegExp | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :x: | | Ruby | RegExp | :heavy_check_mark: | :x: | :heavy_check_mark: | :x: | | Stylus | RegExp | :x: | :x: | :no_entry_sign: | :no_entry_sign: | @@ -53,11 +53,11 @@ To install the latest _commit_ of this extension use the built-in Brackets [Exte Brackets Outline List is licensed under the [MIT license][MIT]. Used thirdparty software: -* [Ionicons][Ionicons] is licensed under the [MIT license][MIT] -* [Lexer][Lexer] is licensed under the [MIT license][MIT] * [Espree][Espree] is licensed under the [BSD 2-Clause License][BSD-2-Clause] +* [Ionicons][Ionicons] is licensed under the [MIT license][MIT] * [PostCSS Safe Parser][PostCSS] is licensed under the [MIT license][MIT] * [htmlparser2][htmlparser2] is licensed under the [MIT license][MIT] +* [php-parser][php-parser] is licensed under the [BSD-3-Clause license][BSD-3-Clause] [Brackets]: http://brackets.io @@ -65,11 +65,12 @@ Used thirdparty software: [Brackets Extension Registry]: https://brackets-registry.aboutweb.com [Brackets npm Registry]: https://github.com/zaggino/brackets-npm-registry -[Ionicons]: http://ionicons.com -[Lexer]: https://github.com/aaditmshah/lexer [Espree]: https://github.com/eslint/espree +[Ionicons]: http://ionicons.com [PostCSS]: https://github.com/postcss/postcss-safe-parser [htmlparser2]: https://github.com/fb55/htmlparser2 +[php-parser]: https://github.com/glayzzle/php-parser -[MIT]: http://opensource.org/licenses/MIT [BSD-2-Clause]: https://opensource.org/licenses/BSD-2-Clause +[BSD-3-Clause]: https://opensource.org/licenses/BSD-3-Clause +[MIT]: http://opensource.org/licenses/MIT diff --git a/src/lexers/PHPLexer.js b/src/lexers/PHPLexer.js index efcf9ad..dddf767 100644 --- a/src/lexers/PHPLexer.js +++ b/src/lexers/PHPLexer.js @@ -1,247 +1,111 @@ define(function PHPLexer(require, exports, module) { "use strict"; - var Lexer = require("thirdparty/lexer"); + var phpParser = require("thirdparty/php-parser"); /** @const {string} Placeholder for unnamed functions. */ var UNNAMED_PLACEHOLDER = "function"; + /** + * Parse a Parameter node. + * @private + * @param {object} argument Parameter node + * @returns {string} String representation of the Parameter. + */ + function _parseArg(argument) { + return "$" + argument.name; + } + + /** + * Traverse a subtree recursivly. + * @private + * @param {object} node AST node + * @param {object[]} list List of objects for the parsed nodes + * @param {number} level Indentation level of the function + * @returns {object[]} List of objects for the parsed nodes + */ + function _traverse(node, list, level) { + if (node.kind === "class") { + list.push({ + type: "class", + name: node.name, + args: [], + modifier: "public", + level: level, + isStatic: false, + line: node.loc.start.line - 1 + }); + level++; + } else if (node.kind === "function") { + list.push({ + type: "function", + name: node.name, + args: node.arguments.map(_parseArg), + modifier: "public", + level: level, + isStatic: false, + line: node.loc.start.line - 1 + }); + level++; + } else if (node.kind === "method") { + list.push({ + type: "function", + name: node.name, + args: node.arguments.map(_parseArg), + modifier: node.visibility, + level: level, + isStatic: node.isStatic, + line: node.loc.start.line - 1 + }); + level++; + } else if (node.kind === "closure") { + list.push({ + type: "function", + name: UNNAMED_PLACEHOLDER, + args: node.arguments.map(_parseArg), + modifier: "unnamed", + level: level, + isStatic: false, + line: node.loc.start.line - 1 + }); + level++; + } + Object.keys(node).forEach(function (prop) { + var children = node[prop]; + if (Array.isArray(children)) { + children.forEach(function (child) { + list = _traverse(child, list, level); + }); + } else if (children instanceof Object) { + list = _traverse(children, list, level); + } + }); + return list; + } + /** * Parse the source and extract the code structure. * @param {string} source the source code. * @returns {object[]} the code structure. */ function parse(source) { - var line = 0; // line number. - var ns = []; // the namespace array. - var literal = true; // check if it's in literal area. - var comment = false; // the comment flag. - var state = []; // the state array. - var modifier = null; // the modifier. - var isStatic = false; // static flag. - var isAbstract = false; // abstract flag. - // saves the results object of the last class that - // extends another or implements an interface. - var lastChildClass = null; - // helper function to peek an item from an array. - var peek = function (array) { - if (array.length > 0) { - return array[array.length - 1]; - } - return null; - }; - var results = []; - var ignored = function () { /* noop */ }; - var lexer = new Lexer(); - lexer - // when it encounters `` structure, turn on literal mode. - .addRule(/\?>/, function () { - literal = true; - }) - // toggle comment if necessary. - .addRule(/\/\*/, function () { - comment = true; - }) - .addRule(/\*\//, function () { - comment = false; - }) - // ignore the comments. - .addRule(/\/\/[^\n]*/, ignored) - // ignore strings (double quotes). - .addRule(/"((?:\\.|[^"\\])*)"/, ignored) - // ignore strings (single quotes). - .addRule(/'((?:\\.|[^'\\])*)'/, ignored) - // ignore execution operator (backticks). - .addRule(/`((?:\\.|[^`\\])*)`/, ignored) - // ignore 'class' word in static::class late static binding. - .addRule(/static::class/, ignored) - // detect abstract modifier, but treat it apart from the visibility modifiers - .addRule(/public|protected|private|abstract/, function (w) { - if (w === "abstract") { - isAbstract = true; - } else { - modifier = w; - } - }) - .addRule(/static/, function () { - isStatic = true; - }) - // when it encounters `function` and literal mode is off, - // 1. push 'function' into state array; - // 2. push a function structure in result. - .addRule(/function/, function () { - if (!literal && !comment) { - state.push("function"); - results.push({ - type: "function", - name: "", - args: [], - modifier: "unnamed", - level: 0, - isStatic: isStatic, - line: line - }); + var ast; + try { + ast = phpParser.parseCode(source, { + parser: { + locations: true, + suppressErrors: true + }, + ast: { + withPositions: true } - }) - // when it encounters `class` and literal mode is off. - // 1. push "class" into state array. - // 2. create a class structure into results array. - .addRule(/class/, function () { - if (!literal && !comment) { - state.push("class"); - results.push({ - type: "class", - name: "", - args: [], - modifier: "public", - level: 0, - isStatic: isStatic, - line: line - }); - } - }) - // support for extended classes and interface implementations - .addRule(/extends|implements/, function () { - if (!literal && !comment) { - if (peek(state) === "class") { - lastChildClass = results.pop(); - state.push("inheriting"); - } - } - }) - // if it's a variable and it's in function args semantics, push it into args array. - .addRule(/\$[0-9a-zA-Z_]+/, function (w) { - if (!literal && !comment) { - if (peek(state) === "args") { - peek(results).args.push(w); - } - // reset modifiers when variable is parsed. - modifier = null; - isStatic = false; - } - }) - // check if it's an identity term. - .addRule(/[0-9a-zA-Z_]+/, function (w) { - var ref; - if (!literal && !comment) { - switch (peek(state)) { - case "function": - ns.push(w); - ref = peek(results); - ref.name = w; - ref.level = ns.length - 1; - ref.modifier = modifier || "public"; - break; - case "class": - ns.push(w); - ref = peek(results); - ref.name = w; - break; - case "inheriting": - state.pop(); - results.push(lastChildClass); - break; - default: - break; - } - // reset modifier when identity term is parsed. - modifier = null; - isStatic = false; - } - }) - // check if it's in function definition, turn on args mode. - .addRule(/\(/, function () { - if (!literal && !comment) { - if (peek(state) === "function") { - var ref = peek(results); - if (ref.modifier === "unnamed") { - ns.push(UNNAMED_PLACEHOLDER); - ref.name = UNNAMED_PLACEHOLDER; - ref.level = ns.length - 1; - } - if (!ref || ref.type !== "function") { - ns.push(UNNAMED_PLACEHOLDER); - results.push({ - type: "function", - name: "", - args: [], - modifier: "unnamed", - level: 0, - isStatic: false, - line: line - }); - } - state.push("args"); - } - } - }) - // turn off args mode. - .addRule(/\)/, function () { - if (!literal && !comment) { - if (peek(state) === "args") { - state.pop(); - } - } - }) - // ignore return types. - .addRule(/:\s*[0-9a-zA-Z_]+/, ignored) - // start function/class body definition or scoped code structure. - .addRule(/{/, function () { - if (!literal && !comment) { - var ref; - if ((ref = peek(state)) === "function" || ref === "class") { - var prefix = state.pop(); - state.push(prefix + ":start"); - } else { - state.push("start"); - } - } - }) - // pop name from namespace array if it's in a namespace. - .addRule(/}/, function () { - if (!literal && !comment && state.length > 0) { - var s = state.pop().split(":")[0]; - if (s === "class" || s === "function") { - ns.pop(); - } - } - }) - // support for abstract methods - .addRule(/;/, function () { - if (!literal && !comment) { - if (peek(state) === "function" && isAbstract) { - state.pop(); - ns.pop(); - isAbstract = false; // reset abstract flag - } else if (peek(state) === "class") { - ns.pop(); - } - } - }) - // support for classes implementing multiple interfaces - .addRule(/,/, function () { - if (!literal && !comment) { - if (peek(state) === "class") { - state.push("inheriting"); - results.pop(); - } - } - }) - // other terms are ignored. - .addRule(/./, ignored); + }); + } catch (error) { + throw new Error("SyntaxError"); + } - // parse the code to the end of the source. - source.split(/\r?\n/).forEach(function (sourceLine) { - lexer.setInput(sourceLine); - lexer.lex(); - // line number increases. - line += 1; - }); - return results; + var result = _traverse(ast, [], 0); + return result; } module.exports = { diff --git a/test/example/php/73-html_class.php b/test/example/php/73-html_class.php index bfee4fd..44e0c61 100644 --- a/test/example/php/73-html_class.php +++ b/test/example/php/73-html_class.php @@ -1,6 +1,6 @@ '.$nr.''; } @@ -13,5 +13,5 @@ function ProcessAction ($vars){} 'size' => 75, 'maxlength' => 200, 'value' => 'Albuquerque, NM - Concrete Field Testing Technician - Grade I', - ), + ) ); diff --git a/test/example/php/strings.php b/test/example/php/strings.php index a48b21c..735ce78 100644 --- a/test/example/php/strings.php +++ b/test/example/php/strings.php @@ -2,3 +2,9 @@ require_once("class.function/\"extends\".'interface'"); $str2 = 'class "function"\'extends interface\''; $command = `class function \`extends\` interface`; + +$multi_line = ' +function myFunc () { + return; +} +'; diff --git a/thirdparty/lexer.js b/thirdparty/lexer.js deleted file mode 100644 index f96d57f..0000000 --- a/thirdparty/lexer.js +++ /dev/null @@ -1,177 +0,0 @@ -/* - Lexer version 1.7.9 - - Copyright (c) 2013 Aadit M Shah - - Permission is hereby granted, free of charge, to any person obtaining a copy - of this software and associated documentation files (the "Software"), to deal - in the Software without restriction, including without limitation the rights - to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - copies of the Software, and to permit persons to whom the Software is - furnished to do so, subject to the following conditions: - - The above copyright notice and this permission notice shall be included in all - copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - SOFTWARE. -*/ - -function Lexer(defunct) { - if (typeof defunct !== "function") defunct = Lexer.defunct; - - var tokens = []; - var rules = []; - var remove = 0; - this.state = 0; - this.index = 0; - this.input = ""; - - this.addRule = function (pattern, action, start) { - var global = pattern.global; - - if (!global) { - var flags = "g"; - if (pattern.multiline) flags += "m"; - if (pattern.ignoreCase) flags += "i"; - pattern = new RegExp(pattern.source, flags); - } - - if (Object.prototype.toString.call(start) !== "[object Array]") start = [0]; - - rules.push({ - pattern: pattern, - global: global, - action: action, - start: start - }); - - return this; - }; - - this.setInput = function (input) { - remove = 0; - this.state = 0; - this.index = 0; - tokens.length = 0; - this.input = input; - return this; - }; - - this.lex = function () { - if (tokens.length) return tokens.shift(); - - this.reject = true; - - while (this.index <= this.input.length) { - var matches = scan.call(this).splice(remove); - var index = this.index; - - while (matches.length) { - if (this.reject) { - var match = matches.shift(); - var result = match.result; - var length = match.length; - this.index += length; - this.reject = false; - remove++; - - var token = match.action.apply(this, result); - if (this.reject) this.index = result.index; - else if (typeof token !== "undefined") { - switch (Object.prototype.toString.call(token)) { - case "[object Array]": - tokens = token.slice(1); - token = token[0]; - default: - if (length) remove = 0; - return token; - } - } - } else break; - } - - var input = this.input; - - if (index < input.length) { - if (this.reject) { - remove = 0; -// var token = defunct.call(this, input.charAt(this.index++)); - var token = defunct.call(this, input.charCodeAt(this.index++)); - if (typeof token !== "undefined") { - if (Object.prototype.toString.call(token) === "[object Array]") { - tokens = token.slice(1); - return token[0]; - } else return token; - } - } else { - if (this.index !== index) remove = 0; - this.reject = true; - } - } else if (matches.length) - this.reject = true; - else break; - } - }; - - function scan() { - var matches = []; - var index = 0; - - var state = this.state; - var lastIndex = this.index; - var input = this.input; - - for (var i = 0, length = rules.length; i < length; i++) { - var rule = rules[i]; - var start = rule.start; - var states = start.length; - - if ((!states || start.indexOf(state) >= 0) || - (state % 2 && states === 1 && !start[0])) { - var pattern = rule.pattern; - pattern.lastIndex = lastIndex; - var result = pattern.exec(input); - - if (result && result.index === lastIndex) { - var j = matches.push({ - result: result, - action: rule.action, - length: result[0].length - }); - - if (rule.global) index = j; - - while (--j > index) { - var k = j - 1; - - if (matches[j].length > matches[k].length) { - var temple = matches[j]; - matches[j] = matches[k]; - matches[k] = temple; - } - } - } - } - } - - return matches; - } -} - -Lexer.defunct = function (chr) { - throw new Error("Unexpected character at index " + (this.index - 1) + ": " + chr); -}; - -if (typeof exports === 'object' && typeof module !== 'undefined') { - module.exports = Lexer; -} else if (typeof define === 'function' && define.amd) { - define(function() { return Lexer; }); -} else { - this.marked = Lexer; -} diff --git a/thirdparty/php-parser.js b/thirdparty/php-parser.js new file mode 100644 index 0000000..4969c5e --- /dev/null +++ b/thirdparty/php-parser.js @@ -0,0 +1,8475 @@ +/* + * php-parser version 2.0.0 + * (browserified with Browserify version 14.3.0) + * + * Copyright (c) 2014, Glayzzle + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * * Neither the name of test-bsd nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.phpparser = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o 1) { + for (var i = 1; i < arguments.length; i++) { + args[i - 1] = arguments[i]; + } + } + queue.push(new Item(fun, args)); + if (queue.length === 1 && !draining) { + runTimeout(drainQueue); + } +}; + +// v8 likes predictible objects +function Item(fun, array) { + this.fun = fun; + this.array = array; +} +Item.prototype.run = function () { + this.fun.apply(null, this.array); +}; +process.title = 'browser'; +process.browser = true; +process.env = {}; +process.argv = []; +process.version = ''; // empty string to avoid regexp issues +process.versions = {}; + +function noop() {} + +process.on = noop; +process.addListener = noop; +process.once = noop; +process.off = noop; +process.removeListener = noop; +process.removeAllListeners = noop; +process.emit = noop; + +process.binding = function (name) { + throw new Error('process.binding is not supported'); +}; + +process.cwd = function () { return '/' }; +process.chdir = function (dir) { + throw new Error('process.chdir is not supported'); +}; +process.umask = function() { return 0; }; + +},{}],2:[function(require,module,exports){ +/*! + * Copyright (C) 2017 Glayzzle (BSD3 License) + * @authors https://github.com/glayzzle/php-parser/graphs/contributors + * @url http://gla*yzzle.com + */ + +var Location = require('./ast/location'); +var Position = require('./ast/position'); + +/** + * ## Class hierarchy + * + * - [Location](#location) + * - [Position](#position) + * - [Node](#node) + * - [Identifier](#identifier) + * - [TraitUse](#traituse) + * - [TraitAlias](#traitalias) + * - [TraitPrecedence](#traitprecedence) + * - [Entry](#entry) + * - [Case](#case) + * - [Label](#label) + * - [Doc](#doc) + * - [Error](#error) + * - [Expression](#expression) + * - [Array](#array) + * - [Variable](#variable) + * - [Variadic](#variadic) + * - [ConstRef](#constref) + * - [Yield](#yield) + * - [YieldFrom](#yieldfrom) + * - [Lookup](#lookup) + * - [PropertyLookup](#propertylookup) + * - [StaticLookup](#staticlookup) + * - [OffsetLookup](#offsetlookup) + * - [Operation](#operation) + * - [Pre](#pre) + * - [Post](#post) + * - [Bin](#bin) + * - [Parenthesis](#parenthesis) + * - [Unary](#unary) + * - [Cast](#cast) + * - [Literal](#literal) + * - [Boolean](#boolean) + * - [String](#string) + * - [Number](#number) + * - [Inline](#inline) + * - [Magic](#magic) + * - [Nowdoc](#nowdoc) + * - [Encapsed](#encapsed) + * - [Statement](#statement) + * - [Eval](#eval) + * - [Exit](#exit) + * - [Halt](#halt) + * - [Clone](#clone) + * - [Declare](#declare) + * - [Global](#global) + * - [Static](#static) + * - [Include](#include) + * - [Assign](#assign) + * - [RetIf](#retif) + * - [If](#if) + * - [Do](#do) + * - [While](#while) + * - [For](#for) + * - [Foreach](#foreach) + * - [Switch](#switch) + * - [Goto](#goto) + * - [Silent](#silent) + * - [Try](#try) + * - [Catch](#catch) + * - [Throw](#throw) + * - [Call](#call) + * - [Closure](#closure) + * - [New](#new) + * - [UseGroup](#usegroup) + * - [UseItem](#useitem) + * - [Block](#block) + * - [Program](#program) + * - [Namespace](#namespace) + * - [Sys](#sys) + * - [Echo](#echo) + * - [List](#list) + * - [Print](#print) + * - [Isset](#isset) + * - [Unset](#unset) + * - [Empty](#empty) + * - [Declaration](#declaration) + * - [Class](#class) + * - [Interface](#interface) + * - [Trait](#trait) + * - [Constant](#constant) + * - [ClassConstant](#classconstant) + * - [Function](#function) + * - [Method](#method) + * - [Parameter](#parameter) + * - [Property](#property) + * --- + */ + +/** + * The AST builder class + * @constructor AST + * @property {Boolean} withPositions - Should locate any node (by default false) + * @property {Boolean} withSource - Should extract the node original code (by default false) + */ +var AST = function(withPositions, withSource) { + this.withPositions = withPositions; + this.withSource = withSource; +}; + +/** + * Create a position node from specified parser + * including it's lexer current state + * @param {Parser} + * @return {Position} + * @private + */ +AST.prototype.position = function(parser) { + return new Position( + parser.lexer.yylloc.first_line, + parser.lexer.yylloc.first_column, + parser.lexer.yylloc.first_offset + ); +}; + +/** + * Prepares an AST node + * @param {String|null} kind - Defines the node type + * (if null, the kind must be passed at the function call) + * @param {Parser} parser - The parser instance (use for extracting locations) + * @return {Function} + */ +AST.prototype.prepare = function(kind, parser) { + var start = null; + if (this.withPositions || this.withSource) { + start = this.position(parser); + } + var self = this; + // returns the node + return function() { + var location = null; + var args = Array.prototype.slice.call(arguments); + if (self.withPositions || self.withSource) { + var src = null; + if (self.withSource) { + src = parser.lexer._input.substring( + start.offset, + parser.lexer.yylloc.prev_offset + ); + } + if (self.withPositions) { + location = new Location(src, start, new Position( + parser.lexer.yylloc.prev_line, + parser.lexer.yylloc.prev_column, + parser.lexer.yylloc.prev_offset + )); + } else { + location = new Location(src, null, null); + } + // last argument is allways the location + args.push(location); + } + // handle lazy kind definitions + if (!kind) { + kind = args.shift(); + } + // build the object + var node = self[kind]; + if (typeof node !== 'function') { + throw new Error('Undefined node "'+kind+'"'); + } + var result = Object.create(node.prototype); + node.apply(result, args); + if ( + result.kind === 'bin' && + result.right && + typeof result.right.precedence === 'function' + ) { + var out = result.right.precedence(result); + if (out) { // shift with precedence + result = out; + } + } else if (result.kind === 'unary') { + var out = result.precedence(result.what); + if (out) { // shift with precedence + result = out; + } + } + return result; + }; +}; + +// Define all AST nodes +[ + require('./ast/array'), + require('./ast/assign'), + require('./ast/bin'), + require('./ast/block'), + require('./ast/boolean'), + require('./ast/break'), + require('./ast/call'), + require('./ast/case'), + require('./ast/cast'), + require('./ast/catch'), + require('./ast/class'), + require('./ast/classconstant'), + require('./ast/clone'), + require('./ast/closure'), + require('./ast/constant'), + require('./ast/constref'), + require('./ast/continue'), + require('./ast/declaration'), + require('./ast/declare'), + require('./ast/do'), + require('./ast/doc'), + require('./ast/echo'), + require('./ast/empty'), + require('./ast/encapsed'), + require('./ast/entry'), + require('./ast/error'), + require('./ast/eval'), + require('./ast/exit'), + require('./ast/expression'), + require('./ast/for'), + require('./ast/foreach'), + require('./ast/function'), + require('./ast/global'), + require('./ast/goto'), + require('./ast/halt'), + require('./ast/identifier'), + require('./ast/if'), + require('./ast/include'), + require('./ast/inline'), + require('./ast/interface'), + require('./ast/isset'), + require('./ast/label'), + require('./ast/list'), + require('./ast/literal'), + require('./ast/lookup'), + require('./ast/magic'), + require('./ast/method'), + require('./ast/namespace'), + require('./ast/new'), + require('./ast/node'), + require('./ast/nowdoc'), + require('./ast/number'), + require('./ast/offsetlookup'), + require('./ast/operation'), + require('./ast/parameter'), + require('./ast/parenthesis'), + require('./ast/post'), + require('./ast/pre'), + require('./ast/print'), + require('./ast/program'), + require('./ast/property'), + require('./ast/propertylookup'), + require('./ast/retif'), + require('./ast/return'), + require('./ast/silent'), + require('./ast/statement'), + require('./ast/static'), + require('./ast/staticlookup'), + require('./ast/string'), + require('./ast/switch'), + require('./ast/sys'), + require('./ast/throw'), + require('./ast/trait'), + require('./ast/traitalias'), + require('./ast/traitprecedence'), + require('./ast/traituse'), + require('./ast/try'), + require('./ast/unary'), + require('./ast/unset'), + require('./ast/usegroup'), + require('./ast/useitem'), + require('./ast/variable'), + require('./ast/variadic'), + require('./ast/while'), + require('./ast/yield'), + require('./ast/yieldfrom') +].forEach(function (ctor) { + var kind = ctor.prototype.constructor.name.toLowerCase(); + if (kind[0] === '_') kind = kind.substring(1); + AST.prototype[kind] = ctor; +}); + +module.exports = AST; + +},{"./ast/array":3,"./ast/assign":4,"./ast/bin":5,"./ast/block":6,"./ast/boolean":7,"./ast/break":8,"./ast/call":9,"./ast/case":10,"./ast/cast":11,"./ast/catch":12,"./ast/class":13,"./ast/classconstant":14,"./ast/clone":15,"./ast/closure":16,"./ast/constant":17,"./ast/constref":18,"./ast/continue":19,"./ast/declaration":20,"./ast/declare":21,"./ast/do":22,"./ast/doc":23,"./ast/echo":24,"./ast/empty":25,"./ast/encapsed":26,"./ast/entry":27,"./ast/error":28,"./ast/eval":29,"./ast/exit":30,"./ast/expression":31,"./ast/for":32,"./ast/foreach":33,"./ast/function":34,"./ast/global":35,"./ast/goto":36,"./ast/halt":37,"./ast/identifier":38,"./ast/if":39,"./ast/include":40,"./ast/inline":41,"./ast/interface":42,"./ast/isset":43,"./ast/label":44,"./ast/list":45,"./ast/literal":46,"./ast/location":47,"./ast/lookup":48,"./ast/magic":49,"./ast/method":50,"./ast/namespace":51,"./ast/new":52,"./ast/node":53,"./ast/nowdoc":54,"./ast/number":55,"./ast/offsetlookup":56,"./ast/operation":57,"./ast/parameter":58,"./ast/parenthesis":59,"./ast/position":60,"./ast/post":61,"./ast/pre":62,"./ast/print":63,"./ast/program":64,"./ast/property":65,"./ast/propertylookup":66,"./ast/retif":67,"./ast/return":68,"./ast/silent":69,"./ast/statement":70,"./ast/static":71,"./ast/staticlookup":72,"./ast/string":73,"./ast/switch":74,"./ast/sys":75,"./ast/throw":76,"./ast/trait":77,"./ast/traitalias":78,"./ast/traitprecedence":79,"./ast/traituse":80,"./ast/try":81,"./ast/unary":82,"./ast/unset":83,"./ast/usegroup":84,"./ast/useitem":85,"./ast/variable":86,"./ast/variadic":87,"./ast/while":88,"./ast/yield":89,"./ast/yieldfrom":90}],3:[function(require,module,exports){ +/*! + * Copyright (C) 2017 Glayzzle (BSD3 License) + * @authors https://github.com/glayzzle/php-parser/graphs/contributors + * @url http://glayzzle.com + */ + +var Expr = require('./expression'); +var KIND = 'array'; + +/** + * Defines an array structure + * @constructor Array + * @extends {Expression} + * @property {Entry[]} items + * @property {boolean} shortForm + */ +var Array = Expr.extends(function Array(shortForm, items, location) { + Expr.apply(this, [KIND, location]); + this.items = items; + this.shortForm = shortForm; +}); + +module.exports = Array; + +},{"./expression":31}],4:[function(require,module,exports){ +/*! + * Copyright (C) 2017 Glayzzle (BSD3 License) + * @authors https://github.com/glayzzle/php-parser/graphs/contributors + * @url http://glayzzle.com + */ + +var Statement = require('./statement'); +var KIND = 'assign'; + +/** + * Assigns a value to the specified target + * @constructor Assign + * @extends {Statement} + * @property {Expression} left + * @property {Expression} right + * @property {String} operator + */ +var Assign = Statement.extends(function Assign(left, right, operator, location) { + Statement.apply(this, [KIND, location]); + this.operator = operator; + this.left = left; + this.right = right; +}); + +module.exports = Assign; + +},{"./statement":70}],5:[function(require,module,exports){ +/*! + * Copyright (C) 2017 Glayzzle (BSD3 License) + * @authors https://github.com/glayzzle/php-parser/graphs/contributors + * @url http://glayzzle.com + */ +"use strict"; + +var Operation = require('./operation'); +var KIND = 'bin'; + +// operators in ascending order of precedence +var binOperatorsPrecedence = [ + ['or'], + ['xor'], + ['and'], + // TODO: assignment / not sure that PHP allows this with expressions + ['retif'], + ['??'], + ['||'], + ['&&'], + ['|'], + ['^'], + ['&'], + ['==', '!=', '===', '!==', /* '<>', */ '<=>'], + ['<', '<=', '>', '>='], + ['<<', '>>'], + ['+', '-', '.'], + ['*', '/', '%'], + ['!'], + ['instanceof'], + // TODO: typecasts + // TODO: [ (array) + // TODO: clone, new +]; + +/* +x OP1 (y OP2 z) +z OP1 (x OP2 y) +z OP2 (x OP1 y) +*/ +/** + * Binary operations + * @constructor Bin + * @extends {Operation} + * @property {String} type + * @property {Expression} left + * @property {Expression} right + */ +var Bin = Operation.extends(function Bin(type, left, right, location) { + Operation.apply(this, [KIND, location]); + this.type = type; + this.left = left; + this.right = right; +}); + +Bin.prototype.precedence = function(node) { + var lLevel = Bin.precedence[node.type]; + var rLevel = Bin.precedence[this.type]; + if (lLevel && rLevel && rLevel < lLevel) { + // shift precedence + node.right = this.left; + this.left = node; + return this; + } +}; + +// define nodes shifting +Bin.precedence = {}; +binOperatorsPrecedence.forEach(function (list, index) { + list.forEach(function (operator) { + Bin.precedence[operator] = index + 1; + }); +}); + +module.exports = Bin; + +},{"./operation":57}],6:[function(require,module,exports){ +/*! + * Copyright (C) 2017 Glayzzle (BSD3 License) + * @authors https://github.com/glayzzle/php-parser/graphs/contributors + * @url http://glayzzle.com + */ + +var Statement = require('./statement'); +var KIND = 'block'; + +/** + * A block statement, i.e., a sequence of statements surrounded by braces. + * @constructor Block + * @extends {Statement} + * @property {Node[]} children + */ +var Block = Statement.extends(function Block(kind, children, location) { + Statement.apply(this, [kind || KIND, location]); + this.children = children; +}); + +module.exports = Block; + +},{"./statement":70}],7:[function(require,module,exports){ +/*! + * Copyright (C) 2017 Glayzzle (BSD3 License) + * @authors https://github.com/glayzzle/php-parser/graphs/contributors + * @url http://glayzzle.com + */ + +var Literal = require('./literal'); +var KIND = 'boolean'; + +/** + * Defines a boolean value (true/false) + * @constructor Boolean + * @extends {Literal} + */ +var Boolean = Literal.extends(function Boolean(value, location) { + Literal.apply(this, [KIND, value, location]); +}); + +module.exports = Boolean; + +},{"./literal":46}],8:[function(require,module,exports){ +/*! + * Copyright (C) 2017 Glayzzle (BSD3 License) + * @authors https://github.com/glayzzle/php-parser/graphs/contributors + * @url http://glayzzle.com + */ +"use strict"; +var Node = require('./node'); +var KIND = 'break'; + +/** + * A break statement + * @constructor Break + * @extends {Node} + * @property {Number|Null} level + */ +var Break = Node.extends(function Break(level, location) { + Node.apply(this, [KIND, location]); + this.level = level; +}); + +module.exports = Break; + +},{"./node":53}],9:[function(require,module,exports){ +/*! + * Copyright (C) 2017 Glayzzle (BSD3 License) + * @authors https://github.com/glayzzle/php-parser/graphs/contributors + * @url http://glayzzle.com + */ +"use strict"; + +var Statement = require('./statement'); +var KIND = 'call'; + +/** + * Executes a call statement + * @constructor Call + * @extends {Statement} + * @property {Identifier|Variable|??} what + * @property {Arguments[]} arguments + */ +var Call = Statement.extends(function Call(what, args, location) { + Statement.apply(this, [KIND, location]); + this.what = what; + this.arguments = args; +}); + +module.exports = Call; + +},{"./statement":70}],10:[function(require,module,exports){ +/*! + * Copyright (C) 2017 Glayzzle (BSD3 License) + * @authors https://github.com/glayzzle/php-parser/graphs/contributors + * @url http://glayzzle.com + */ +"use strict"; +var Node = require('./node'); +var KIND = 'case'; + +/** + * A switch case statement + * @constructor Case + * @extends {Node} + * @property {Expression|null} test - if null, means that the default case + * @property {Block|null} body + */ +var Case = Node.extends(function Case(test, body, location) { + Node.apply(this, [KIND, location]); + this.test = test; + this.body = body; +}); + +module.exports = Case; + +},{"./node":53}],11:[function(require,module,exports){ +/*! + * Copyright (C) 2017 Glayzzle (BSD3 License) + * @authors https://github.com/glayzzle/php-parser/graphs/contributors + * @url http://glayzzle.com + */ +"use strict"; + +var Operation = require('./operation'); +var KIND = 'cast'; + +/** + * Binary operations + * @constructor Cast + * @extends {Operation} + * @property {String} type + * @property {Expression} what + */ +var Cast = Operation.extends(function Cast(type, what, location) { + Operation.apply(this, [KIND, location]); + this.type = type; + this.what = what; +}); + +module.exports = Cast; + +},{"./operation":57}],12:[function(require,module,exports){ +/*! + * Copyright (C) 2017 Glayzzle (BSD3 License) + * @authors https://github.com/glayzzle/php-parser/graphs/contributors + * @url http://glayzzle.com + */ +"use strict"; + +var Statement = require('./statement'); +var KIND = 'catch'; + +/** + * Defines a catch statement + * @constructor Catch + * @extends {Statement} + * @property {Identifier[]} what + * @property {Variable} variable + * @property {Statement} body + * @see http://php.net/manual/en/language.exceptions.php + */ +var Catch = Statement.extends(function Catch(body, what, variable, location) { + Statement.apply(this, [KIND, location]); + this.body = body; + this.what = what; + this.variable = variable; +}); + +module.exports = Catch; + +},{"./statement":70}],13:[function(require,module,exports){ +/*! + * Copyright (C) 2017 Glayzzle (BSD3 License) + * @authors https://github.com/glayzzle/php-parser/graphs/contributors + * @url http://glayzzle.com + */ + +var Declaration = require('./declaration'); +var KIND = 'class'; + + +/** + * A class definition + * @constructor Class + * @extends {Declaration} + * @property {Identifier|null} extends + * @property {Identifier[]} implements + * @property {Declaration[]} body + * @property {boolean} isAnonymous + * @property {boolean} isAbstract + * @property {boolean} isFinal + */ +var Class = Declaration.extends(function Class(name, ext, impl, body, flags, location) { + Declaration.apply(this, [KIND, name, location]); + this.isAnonymous = name ? false : true; + this.extends = ext; + this.implements = impl; + this.body = body; + this.parseFlags(flags); +}); + +module.exports = Class; + +},{"./declaration":20}],14:[function(require,module,exports){ +/*! + * Copyright (C) 2017 Glayzzle (BSD3 License) + * @authors https://github.com/glayzzle/php-parser/graphs/contributors + * @url http://glayzzle.com + */ + +var Constant = require('./constant'); +var KIND = 'classconstant'; + +/** + * Defines a class/interface/trait constant + * @constructor ClassConstant + * @extends {Constant} + * @property {boolean} isStatic + * @property {string} visibility + */ +var ClassConstant = Constant.extends(function ClassConstant(name, value, flags, location) { + Constant.apply(this, [name, value, location]); + this.kind = KIND; + this.parseFlags(flags); +}); + +module.exports = ClassConstant; + +},{"./constant":17}],15:[function(require,module,exports){ +/*! + * Copyright (C) 2017 Glayzzle (BSD3 License) + * @authors https://github.com/glayzzle/php-parser/graphs/contributors + * @url http://glayzzle.com + */ + +var Statement = require('./statement'); +var KIND = 'clone'; + +/** + * Defines a clone call + * @constructor Clone + * @extends {Statement} + * @property {Expression} what + */ +var Clone = Statement.extends(function Clone(what, location) { + Statement.apply(this, [KIND, location]); + this.what = what; +}); + +module.exports = Clone; + +},{"./statement":70}],16:[function(require,module,exports){ +/*! + * Copyright (C) 2017 Glayzzle (BSD3 License) + * @authors https://github.com/glayzzle/php-parser/graphs/contributors + * @url http://glayzzle.com + */ +"use strict"; +var Statement = require('./statement'); +var KIND = 'closure'; + +/** + * Defines a closure + * @constructor Closure + * @extends {Statement} + * @property {Parameter[]} arguments + * @property {Variable[]} uses + * @property {Identifier} type + * @property {boolean} byref + * @property {boolean} nullable + * @property {Block|null} body + */ +var Closure = Statement.extends(function Closure(args, byref, uses, type, nullable, location) { + Statement.apply(this, [KIND, location]); + this.uses = uses; + this.arguments = args; + this.byref = byref; + this.type = type; + this.nullable = nullable; + this.body = null; +}); + +module.exports = Closure; + +},{"./statement":70}],17:[function(require,module,exports){ +/*! + * Copyright (C) 2017 Glayzzle (BSD3 License) + * @authors https://github.com/glayzzle/php-parser/graphs/contributors + * @url http://glayzzle.com + */ + +var Declaration = require('./declaration'); +var KIND = 'constant'; + +/** + * Defines a namespace constant + * @constructor Constant + * @extends {Declaration} + * @property {Node|null} value + */ +var Constant = Declaration.extends(function Constant(name, value, location) { + Declaration.apply(this, [KIND, name, location]); + this.value = value; +}); + +module.exports = Constant; + +},{"./declaration":20}],18:[function(require,module,exports){ +/*! + * Copyright (C) 2017 Glayzzle (BSD3 License) + * @authors https://github.com/glayzzle/php-parser/graphs/contributors + * @url http://glayzzle.com + */ + +var Expr = require('./expression'); +var KIND = 'constref'; + +/** + * A constant reference + * @constructor ConstRef + * @extends {Expression} + * @property {String|Node} name + */ +var ConstRef = Expr.extends(function ConstRef(identifier, location) { + Expr.apply(this, [KIND, location]); + this.name = identifier; +}); + +module.exports = ConstRef; + +},{"./expression":31}],19:[function(require,module,exports){ +/*! + * Copyright (C) 2017 Glayzzle (BSD3 License) + * @authors https://github.com/glayzzle/php-parser/graphs/contributors + * @url http://glayzzle.com + */ +"use strict"; +var Node = require('./node'); +var KIND = 'continue'; + +/** + * A continue statement + * @constructor Continue + * @extends {Node} + * @property {Number|Null} level + */ +var Continue = Node.extends(function Continue(level, location) { + Node.apply(this, [KIND, location]); + this.level = level; +}); + +module.exports = Continue; + +},{"./node":53}],20:[function(require,module,exports){ +/*! + * Copyright (C) 2017 Glayzzle (BSD3 License) + * @authors https://github.com/glayzzle/php-parser/graphs/contributors + * @url http://glayzzle.com + */ + +var Statement = require('./statement'); +var KIND = 'declaration'; + +var IS_PUBLIC = 'public'; +var IS_PROTECTED = 'protected'; +var IS_PRIVATE = 'private'; + +/** + * A declaration statement (function, class, interface...) + * @constructor Declaration + * @extends {Statement} + * @property {string} name + */ +var Declaration = Statement.extends(function Declaration(kind, name, location) { + Statement.apply(this, [kind || KIND, location]); + this.name = name; +}); + +/** + * Generic flags parser + * @param {Integer[]} flags + * @return {void} + */ +Declaration.prototype.parseFlags = function(flags) { + this.isAbstract = flags[2] === 1; + this.isFinal = flags[2] === 2; + if (this.kind !== 'class') { + if (flags[0] === 0) { + this.visibility = IS_PUBLIC; + } else if (flags[0] === 1) { + this.visibility = IS_PROTECTED; + } else if (flags[0] === 2) { + this.visibility = IS_PRIVATE; + } + this.isStatic = flags[1] === 1; + } +}; + +module.exports = Declaration; + +},{"./statement":70}],21:[function(require,module,exports){ +/*! + * Copyright (C) 2017 Glayzzle (BSD3 License) + * @authors https://github.com/glayzzle/php-parser/graphs/contributors + * @url http://glayzzle.com + */ + +var Block = require('./block'); +var KIND = 'declare'; + +/** + * The declare construct is used to set execution directives for a block of code + * @constructor Declare + * @extends {Block} + * @property {Expression[]} what + * @property {String} mode + * @see http://php.net/manual/en/control-structures.declare.php + */ +var Declare = Block.extends(function Declare(what, body, mode, location) { + Block.apply(this, [KIND, body, location]); + this.what = what; + this.mode = mode; +}); + + +/** + * The node is declared as a short tag syntax : + * ```php + * bar_$baz; + * ``` + * @constant {String} TYPE_OFFSET - `offset` + */ +Encapsed.TYPE_OFFSET = 'offset'; + + +module.exports = Encapsed; + +},{"./literal":46}],27:[function(require,module,exports){ +/*! + * Copyright (C) 2017 Glayzzle (BSD3 License) + * @authors https://github.com/glayzzle/php-parser/graphs/contributors + * @url http://glayzzle.com + */ + +var Node = require('./node'); +var KIND = 'entry'; + +/** + * An array entry + * @constructor Entry + * @extends {Node} + * @property {Node|null} key + * @property {Node} value + */ +var Entry = Node.extends(function Entry(key, value, location) { + Node.apply(this, [KIND, location]); + this.key = key; + this.value = value; +}); + +module.exports = Entry; + +},{"./node":53}],28:[function(require,module,exports){ +/*! + * Copyright (C) 2017 Glayzzle (BSD3 License) + * @authors https://github.com/glayzzle/php-parser/graphs/contributors + * @url http://glayzzle.com + */ + +var Node = require('./node'); +var KIND = 'error'; + + +/** + * Defines an error node (used only on silentMode) + * @constructor Error + * @extends {Node} + * @property {string} message + * @property {number} line + * @property {number|string} token + * @property {string|array} expected + */ +var Error = Node.extends(function Error(message, token, line, expected, location) { + Node.apply(this, [KIND, location]); + this.message = message; + this.token = token; + this.line = line; + this.expected = expected; +}); + +module.exports = Error; + +},{"./node":53}],29:[function(require,module,exports){ +/*! + * Copyright (C) 2017 Glayzzle (BSD3 License) + * @authors https://github.com/glayzzle/php-parser/graphs/contributors + * @url http://glayzzle.com + */ + +var Statement = require('./statement'); +var KIND = 'eval'; + +/** + * Defines an eval statement + * @constructor Eval + * @extends {Statement} + * @property {Node} source + */ +var Eval = Statement.extends(function Eval(source, location) { + Statement.apply(this, [KIND, location]); + this.source = source; +}); + +module.exports = Eval; + +},{"./statement":70}],30:[function(require,module,exports){ +/*! + * Copyright (C) 2017 Glayzzle (BSD3 License) + * @authors https://github.com/glayzzle/php-parser/graphs/contributors + * @url http://glayzzle.com + */ + +var Statement = require('./statement'); +var KIND = 'exit'; + +/** + * Defines an exit / die call + * @constructor Exit + * @extends {Statement} + * @property {Node|null} status + */ +var Exit = Statement.extends(function Exit(status, location) { + Statement.apply(this, [KIND, location]); + this.status = status; +}); + +module.exports = Exit; + +},{"./statement":70}],31:[function(require,module,exports){ +/*! + * Copyright (C) 2017 Glayzzle (BSD3 License) + * @authors https://github.com/glayzzle/php-parser/graphs/contributors + * @url http://glayzzle.com + */ + +var Node = require('./node'); +var KIND = 'expression'; + +/** + * Any expression node. Since the left-hand side of an assignment may + * be any expression in general, an expression can also be a pattern. + * @constructor Expression + * @extends {Node} + */ +var Expression = Node.extends(function Expression(kind, location) { + Node.apply(this, [kind || KIND, location]); +}); + +module.exports = Expression; + +},{"./node":53}],32:[function(require,module,exports){ +/*! + * Copyright (C) 2017 Glayzzle (BSD3 License) + * @authors https://github.com/glayzzle/php-parser/graphs/contributors + * @url http://glayzzle.com + */ +"use strict"; + +var Statement = require('./statement'); +var KIND = 'for'; + +/** + * Defines a for iterator + * @constructor For + * @extends {Statement} + * @property {Expression[]} init + * @property {Expression[]} test + * @property {Expression[]} increment + * @property {Statement} body + * @property {boolean} shortForm + * @see http://php.net/manual/en/control-structures.for.php + */ +var For = Statement.extends(function For(init, test, increment, body, shortForm, location) { + Statement.apply(this, [KIND, location]); + this.init = init; + this.test = test; + this.increment = increment; + this.shortForm = shortForm; + this.body = body; +}); + +module.exports = For; + +},{"./statement":70}],33:[function(require,module,exports){ +/*! + * Copyright (C) 2017 Glayzzle (BSD3 License) + * @authors https://github.com/glayzzle/php-parser/graphs/contributors + * @url http://glayzzle.com + */ +"use strict"; + +var Statement = require('./statement'); +var KIND = 'foreach'; + +/** + * Defines a foreach iterator + * @constructor Foreach + * @extends {Statement} + * @property {Expression} source + * @property {Expression|null} key + * @property {Expression} value + * @property {Statement} body + * @property {boolean} shortForm + * @see http://php.net/manual/en/control-structures.foreach.php + */ +var Foreach = Statement.extends(function Foreach(source, key, value, body, shortForm, location) { + Statement.apply(this, [KIND, location]); + this.source = source; + this.key = key; + this.value = value; + this.shortForm = shortForm; + this.body = body; +}); + +module.exports = Foreach; + +},{"./statement":70}],34:[function(require,module,exports){ +/*! + * Copyright (C) 2017 Glayzzle (BSD3 License) + * @authors https://github.com/glayzzle/php-parser/graphs/contributors + * @url http://glayzzle.com + */ + +var Declaration = require('./declaration'); +var KIND = 'function'; + +/** + * Defines a classic function + * @constructor Function + * @extends {Declaration} + * @property {Parameter[]} arguments + * @property {Identifier} type + * @property {boolean} byref + * @property {boolean} nullable + * @property {Block|null} body + */ +var fn = Declaration.extends(function _Function(name, args, byref, type, nullable, location) { + Declaration.apply(this, [KIND, name, location]); + this.arguments = args; + this.byref = byref; + this.type = type; + this.nullable = nullable; + this.body = null; +}); +module.exports = fn; + +},{"./declaration":20}],35:[function(require,module,exports){ +/*! + * Copyright (C) 2017 Glayzzle (BSD3 License) + * @authors https://github.com/glayzzle/php-parser/graphs/contributors + * @url http://glayzzle.com + */ +"use strict"; +var Statement = require('./statement'); +var KIND = 'global'; + +/** + * Imports a variable from the global scope + * @constructor Global + * @extends {Statement} + * @property {Variable[]} items + */ +var Global = Statement.extends(function Global(items, location) { + Statement.apply(this, [KIND, location]); + this.items = items; +}); + +module.exports = Global; + +},{"./statement":70}],36:[function(require,module,exports){ +/*! + * Copyright (C) 2017 Glayzzle (BSD3 License) + * @authors https://github.com/glayzzle/php-parser/graphs/contributors + * @url http://glayzzle.com + */ +"use strict"; + +var Statement = require('./statement'); +var KIND = 'goto'; + +/** + * Defines goto statement + * @constructor Goto + * @extends {Statement} + * @property {String} label + * @see {Label} + */ +var Goto = Statement.extends(function Goto(label, location) { + Statement.apply(this, [KIND, location]); + this.label = label; +}); + +module.exports = Goto; + +},{"./statement":70}],37:[function(require,module,exports){ +/*! + * Copyright (C) 2017 Glayzzle (BSD3 License) + * @authors https://github.com/glayzzle/php-parser/graphs/contributors + * @url http://glayzzle.com + */ +"use strict"; + +var Statement = require('./statement'); +var KIND = 'halt'; + +/** + * Halts the compiler execution + * @constructor Halt + * @extends {Statement} + * @property {String} after - String after the halt statement + * @see http://php.net/manual/en/function.halt-compiler.php + */ +var Halt = Statement.extends(function Halt(after, location) { + Statement.apply(this, [KIND, location]); + this.after = after; +}); + +module.exports = Halt; + +},{"./statement":70}],38:[function(require,module,exports){ +/*! + * Copyright (C) 2017 Glayzzle (BSD3 License) + * @authors https://github.com/glayzzle/php-parser/graphs/contributors + * @url http://glayzzle.com + */ + +var Node = require('./node'); +var KIND = 'identifier'; + +/** + * Defines an identifier node + * @constructor Identifier + * @extends {Node} + * @property {string} name + * @property {string} resolution + */ +var Identifier = Node.extends(function Identifier(name, isRelative, location) { + Node.apply(this, [KIND, location]); + if (isRelative) { + this.resolution = Identifier.RELATIVE_NAME; + } else if (name.length === 1) { + this.resolution = Identifier.UNQUALIFIED_NAME; + } else if (name[0] === '') { + this.resolution = Identifier.FULL_QUALIFIED_NAME; + } else { + this.resolution = Identifier.QUALIFIED_NAME; + } + this.name = name.join('\\'); +}); + +/** + * This is an identifier without a namespace separator, such as Foo + * @constant {String} UNQUALIFIED_NAME + */ +Identifier.UNQUALIFIED_NAME = 'uqn'; +/** + * This is an identifier with a namespace separator, such as Foo\Bar + * @constant {String} QUALIFIED_NAME + */ +Identifier.QUALIFIED_NAME = 'qn'; +/** + * This is an identifier with a namespace separator that begins with + * a namespace separator, such as \Foo\Bar. The namespace \Foo is also + * a fully qualified name. + * @constant {String} FULL_QUALIFIED_NAME + */ +Identifier.FULL_QUALIFIED_NAME = 'fqn'; +/** + * This is an identifier starting with namespace, such as namespace\Foo\Bar. + * @constant {String} RELATIVE_NAME + */ +Identifier.RELATIVE_NAME = 'rn'; + + +module.exports = Identifier; + +},{"./node":53}],39:[function(require,module,exports){ +/*! + * Copyright (C) 2017 Glayzzle (BSD3 License) + * @authors https://github.com/glayzzle/php-parser/graphs/contributors + * @url http://glayzzle.com + */ +"use strict"; + +var Statement = require('./statement'); +var KIND = 'if'; + +/** + * Defines a if statement + * @constructor If + * @extends {Statement} + * @property {Expression} test + * @property {Block} body + * @property {Block|If|null} alternate + * @property {boolean} shortForm + */ +var If = Statement.extends(function If(test, body, alternate, shortForm, location) { + Statement.apply(this, [KIND, location]); + this.test = test; + this.body = body; + this.alternate = alternate; + this.shortForm = shortForm; +}); + +module.exports = If; + +},{"./statement":70}],40:[function(require,module,exports){ +/*! + * Copyright (C) 2017 Glayzzle (BSD3 License) + * @authors https://github.com/glayzzle/php-parser/graphs/contributors + * @url http://glayzzle.com + */ + +var Statement = require('./statement'); +var KIND = 'include'; + +/** + * Defines system include call + * @constructor Include + * @extends {Statement} + * @property {Node} target + * @property {boolean} once + * @property {boolean} require + */ +var Include = Statement.extends(function Include(once, require, target, location) { + Statement.apply(this, [KIND, location]); + this.once = once; + this.require = require; + this.target = target; +}); + +module.exports = Include; + +},{"./statement":70}],41:[function(require,module,exports){ +/*! + * Copyright (C) 2017 Glayzzle (BSD3 License) + * @authors https://github.com/glayzzle/php-parser/graphs/contributors + * @url http://glayzzle.com + */ + +var Literal = require('./literal'); +var KIND = 'inline'; + +/** + * Defines inline html output (treated as echo output) + * @constructor Inline + * @extends {Literal} + */ +var Inline = Literal.extends(function Inline(value, location) { + Literal.apply(this, [KIND, value, location]); +}); + +module.exports = Inline; + +},{"./literal":46}],42:[function(require,module,exports){ +/*! + * Copyright (C) 2017 Glayzzle (BSD3 License) + * @authors https://github.com/glayzzle/php-parser/graphs/contributors + * @url http://glayzzle.com + */ + +var Declaration = require('./declaration'); +var KIND = 'interface'; + + +/** + * An interface definition + * @constructor Interface + * @extends {Declaration} + * @property {Identifier[]} extends + * @property {Declaration[]} body + */ +var Interface = Declaration.extends(function Interface(name, ext, body, location) { + Declaration.apply(this, [KIND, name, location]); + this.extends = ext; + this.body = body; +}); + +module.exports = Interface; + +},{"./declaration":20}],43:[function(require,module,exports){ +/*! + * Copyright (C) 2017 Glayzzle (BSD3 License) + * @authors https://github.com/glayzzle/php-parser/graphs/contributors + * @url http://glayzzle.com + */ + +var Sys = require('./sys'); +var KIND = 'isset'; + +/** + * Defines an isset call + * @constructor Isset + * @extends {Sys} + */ +var Isset = Sys.extends(function Isset(args, location) { + Sys.apply(this, [KIND, args, location]); +}); + +module.exports = Isset; + +},{"./sys":75}],44:[function(require,module,exports){ +/*! + * Copyright (C) 2017 Glayzzle (BSD3 License) + * @authors https://github.com/glayzzle/php-parser/graphs/contributors + * @url http://glayzzle.com + */ +"use strict"; +var Node = require('./node'); +var KIND = 'label'; + +/** + * A label statement (referenced by goto) + * @constructor Label + * @extends {Node} + * @property {String} name + */ +var Label = Node.extends(function Label(name, location) { + Node.apply(this, [KIND, location]); + this.name = name; +}); + +module.exports = Label; + +},{"./node":53}],45:[function(require,module,exports){ +/*! + * Copyright (C) 2017 Glayzzle (BSD3 License) + * @authors https://github.com/glayzzle/php-parser/graphs/contributors + * @url http://glayzzle.com + */ + +var Sys = require('./sys'); +var KIND = 'list'; + +/** + * Defines list assignment + * @constructor List + * @extends {Sys} + */ +var List = Sys.extends(function List(args, location) { + Sys.apply(this, [KIND, args, location]); +}); + +module.exports = List; + +},{"./sys":75}],46:[function(require,module,exports){ +/*! + * Copyright (C) 2017 Glayzzle (BSD3 License) + * @authors https://github.com/glayzzle/php-parser/graphs/contributors + * @url http://glayzzle.com + */ + +var Expr = require('./expression'); +var KIND = 'literal'; + +/** + * Defines an array structure + * @constructor Literal + * @extends {Expression} + * @property {Node|string|number|boolean|null} value + */ +var Literal = Expr.extends(function Literal(kind, value, location) { + Expr.apply(this, [kind || KIND, location]); + this.value = value; +}); + +module.exports = Literal; + +},{"./expression":31}],47:[function(require,module,exports){ +/*! + * Copyright (C) 2017 Glayzzle (BSD3 License) + * @authors https://github.com/glayzzle/php-parser/graphs/contributors + * @url http://glayzzle.com + */ + +/** + * Defines the location of the node (with it's source contents as string) + * @constructor Location + * @property {String|null} source + * @property {Position} start + * @property {Position} end + */ +var Location = function(source, start, end) { + this.source = source; + this.start = start; + this.end = end; +}; + +module.exports = Location; + +},{}],48:[function(require,module,exports){ +/*! + * Copyright (C) 2017 Glayzzle (BSD3 License) + * @authors https://github.com/glayzzle/php-parser/graphs/contributors + * @url http://glayzzle.com + */ + +var Expr = require('./expression'); +var KIND = 'lookup'; + +/** + * Lookup on an offset in the specified object + * @constructor Lookup + * @extends {Expression} + * @property {Expression} what + * @property {Expression} offset + */ +var Lookup = Expr.extends(function Lookup(kind, what, offset, location) { + Expr.apply(this, [kind || KIND, location]); + this.what = what; + this.offset = offset; +}); + +module.exports = Lookup; + +},{"./expression":31}],49:[function(require,module,exports){ +/*! + * Copyright (C) 2017 Glayzzle (BSD3 License) + * @authors https://github.com/glayzzle/php-parser/graphs/contributors + * @url http://glayzzle.com + */ + +var Literal = require('./literal'); +var KIND = 'magic'; + +/** + * Defines magic constant + * @constructor Magic + * @extends {Literal} + */ +var Magic = Literal.extends(function Magic(value, location) { + Literal.apply(this, [KIND, value, location]); +}); + +module.exports = Magic; + +},{"./literal":46}],50:[function(require,module,exports){ +/*! + * Copyright (C) 2017 Glayzzle (BSD3 License) + * @authors https://github.com/glayzzle/php-parser/graphs/contributors + * @url http://glayzzle.com + */ + +var fn = require('./function'); +var KIND = 'method'; + +/** + * Defines a class/interface/trait method + * @constructor Method + * @extends {Function} + * @property {boolean} isAbstract + * @property {boolean} isFinal + * @property {boolean} isStatic + * @property {string} visibility + */ +var Method = fn.extends(function Method() { + fn.apply(this, arguments); + this.kind = KIND; +}); + +module.exports = Method; + +},{"./function":34}],51:[function(require,module,exports){ +/*! + * Copyright (C) 2017 Glayzzle (BSD3 License) + * @authors https://github.com/glayzzle/php-parser/graphs/contributors + * @url http://glayzzle.com + */ + +var Block = require('./block'); +var Identifier = require('./identifier'); +var KIND = 'namespace'; + +/** + * The main program node + * @constructor Namespace + * @extends {Block} + * @property {String} name + * @property {Boolean} withBrackets + */ +var Namespace = Block.extends(function Namespace(name, children, withBrackets, location) { + Block.apply(this, [KIND, children, location]); + this.name = name; + this.withBrackets = withBrackets || false; +}); + +module.exports = Namespace; + +},{"./block":6,"./identifier":38}],52:[function(require,module,exports){ +/*! + * Copyright (C) 2017 Glayzzle (BSD3 License) + * @authors https://github.com/glayzzle/php-parser/graphs/contributors + * @url http://glayzzle.com + */ +"use strict"; + +var Statement = require('./statement'); +var KIND = 'new'; + +/** + * Creates a new instance of the specified class + * @constructor New + * @extends {Statement} + * @property {Identifier|Variable|Class} what + * @property {Arguments[]} arguments + */ +var New = Statement.extends(function New(what, args, location) { + Statement.apply(this, [KIND, location]); + this.what = what; + this.arguments = args; +}); + +module.exports = New; + +},{"./statement":70}],53:[function(require,module,exports){ +/*! + * Copyright (C) 2017 Glayzzle (BSD3 License) + * @authors https://github.com/glayzzle/php-parser/graphs/contributors + * @url http://glayzzle.com + */ + +/** + * A generic AST node + * @constructor Node + * @property {Location|null} loc + * @property {String} kind + */ +var Node = function Node(kind, location) { + this.kind = kind; + if(location) { + this.loc = location; + } +}; + +/** + * Helper for extending the Node class + * @param {Function} constructor + * @return {Function} + */ +Node.extends = function(constructor) { + constructor.prototype = Object.create(this.prototype); + constructor.extends = this.extends; + constructor.prototype.constructor = constructor; + return constructor; +}; + +module.exports = Node; + +},{}],54:[function(require,module,exports){ +/*! + * Copyright (C) 2017 Glayzzle (BSD3 License) + * @authors https://github.com/glayzzle/php-parser/graphs/contributors + * @url http://glayzzle.com + */ + +var Literal = require('./literal'); +var KIND = 'nowdoc'; + +/** + * Defines a nowdoc string + * @constructor String + * @extends {Literal} + * @property {String} label + + */ +var Nowdoc = Literal.extends(function Nowdoc(value, label, location) { + Literal.apply(this, [KIND, value, location]); + this.label = label; +}); + +module.exports = Nowdoc; + +},{"./literal":46}],55:[function(require,module,exports){ +/*! + * Copyright (C) 2017 Glayzzle (BSD3 License) + * @authors https://github.com/glayzzle/php-parser/graphs/contributors + * @url http://glayzzle.com + */ + +var Literal = require('./literal'); +var KIND = 'number'; + +/** + * Defines a numeric value + * @constructor Number + * @extends {Literal} + */ +var _Number = Literal.extends(function Number(value, location) { + Literal.apply(this, [KIND, value, location]); +}); + +module.exports = _Number; + +},{"./literal":46}],56:[function(require,module,exports){ +/*! + * Copyright (C) 2017 Glayzzle (BSD3 License) + * @authors https://github.com/glayzzle/php-parser/graphs/contributors + * @url http://glayzzle.com + */ +"use strict"; +var Lookup = require('./lookup'); +var KIND = 'offsetlookup'; + +/** + * Lookup on an offset in an array + * @constructor OffsetLookup + * @extends {Lookup} + */ +var OffsetLookup = Lookup.extends(function OffsetLookup(what, offset, location) { + Lookup.apply(this, [KIND, what, offset, location]); +}); + +module.exports = OffsetLookup; + +},{"./lookup":48}],57:[function(require,module,exports){ +/*! + * Copyright (C) 2017 Glayzzle (BSD3 License) + * @authors https://github.com/glayzzle/php-parser/graphs/contributors + * @url http://glayzzle.com + */ +"use strict"; + +var Expr = require('./expression'); +var KIND = 'operation'; + +/** + * Defines binary operations + * @constructor Operation + * @extends {Expression} + */ +var Operation = Expr.extends(function Operation(kind, location) { + Expr.apply(this, [kind || KIND, location]); +}); + +module.exports = Operation; + +},{"./expression":31}],58:[function(require,module,exports){ +/*! + * Copyright (C) 2017 Glayzzle (BSD3 License) + * @authors https://github.com/glayzzle/php-parser/graphs/contributors + * @url http://glayzzle.com + */ + +var Declaration = require('./declaration'); +var KIND = 'parameter'; + +/** + * Defines a function parameter + * @constructor Parameter + * @extends {Declaration} + * @property {Identifier|null} type + * @property {Node|null} value + * @property {boolean} byref + * @property {boolean} variadic + * @property {boolean} nullable + */ +var Parameter = Declaration.extends(function Parameter(name, type, value, isRef, isVariadic, nullable, location) { + Declaration.apply(this, [KIND, name, location]); + this.value = value; + this.type = type; + this.byref = isRef; + this.variadic = isVariadic; + this.nullable = nullable; +}); + +module.exports = Parameter; + +},{"./declaration":20}],59:[function(require,module,exports){ +/*! + * Copyright (C) 2017 Glayzzle (BSD3 License) + * @authors https://github.com/glayzzle/php-parser/graphs/contributors + * @url http://glayzzle.com + */ +"use strict"; + +var Operation = require('./operation'); +var KIND = 'parenthesis'; + +/** + * Parenthesis encapsulation `(... expr ...)` + * @constructor Parenthesis + * @extends {Operation} + * @property {Expression} inner + */ +var Parenthesis = Operation.extends(function Parenthesis(inner, location) { + Operation.apply(this, [KIND, location]); + this.inner = inner; +}); + +module.exports = Parenthesis; + +},{"./operation":57}],60:[function(require,module,exports){ +/*! + * Copyright (C) 2017 Glayzzle (BSD3 License) + * @authors https://github.com/glayzzle/php-parser/graphs/contributors + * @url http://glayzzle.com + */ + +/** + * Each Position object consists of a line number (1-indexed) and a column number (0-indexed): + * @constructor Position + * @property {Number} line + * @property {Number} column + * @property {Number} offset + */ +var Position = function(line, column, offset) { + this.line = line; + this.column = column; + this.offset = offset; +}; + +module.exports = Position; + +},{}],61:[function(require,module,exports){ +/*! + * Copyright (C) 2017 Glayzzle (BSD3 License) + * @authors https://github.com/glayzzle/php-parser/graphs/contributors + * @url http://glayzzle.com + */ +"use strict"; + +var Operation = require('./operation'); +var KIND = 'post'; + +/** + * Defines a post operation `$i++` or `$i--` + * @constructor Post + * @extends {Operation} + * @property {String} type + * @property {Variable} what + */ +var Post = Operation.extends(function Post(type, what, location) { + Operation.apply(this, [KIND, location]); + this.type = type; + this.what = what; +}); + +module.exports = Post; + +},{"./operation":57}],62:[function(require,module,exports){ +/*! + * Copyright (C) 2017 Glayzzle (BSD3 License) + * @authors https://github.com/glayzzle/php-parser/graphs/contributors + * @url http://glayzzle.com + */ +"use strict"; + +var Operation = require('./operation'); +var KIND = 'pre'; + +/** + * Defines a pre operation `++$i` or `--$i` + * @constructor Pre + * @extends {Operation} + * @property {String} type + * @property {Variable} what + */ +var Pre = Operation.extends(function Pre(type, what, location) { + Operation.apply(this, [KIND, location]); + this.type = type; + this.what = what; +}); + +module.exports = Pre; + +},{"./operation":57}],63:[function(require,module,exports){ +/*! + * Copyright (C) 2017 Glayzzle (BSD3 License) + * @authors https://github.com/glayzzle/php-parser/graphs/contributors + * @url http://glayzzle.com + */ + +var Sys = require('./sys'); +var KIND = 'print'; + +/** + * Outputs + * @constructor Print + * @extends {Sys} + */ +var Print = Sys.extends(function Print(args, location) { + Sys.apply(this, [KIND, args, location]); +}); + +module.exports = Print; + +},{"./sys":75}],64:[function(require,module,exports){ +/*! + * Copyright (C) 2017 Glayzzle (BSD3 License) + * @authors https://github.com/glayzzle/php-parser/graphs/contributors + * @url http://glayzzle.com + */ + +var Block = require('./block'); +var KIND = 'program'; + +/** + * The main program node + * @constructor Program + * @extends {Block} + * @property {Error[]} errors + */ +var Program = Block.extends(function Program(children, errors, location) { + Block.apply(this, [KIND, children, location]); + this.errors = errors; +}); + +module.exports = Program; + +},{"./block":6}],65:[function(require,module,exports){ +/*! + * Copyright (C) 2017 Glayzzle (BSD3 License) + * @authors https://github.com/glayzzle/php-parser/graphs/contributors + * @url http://glayzzle.com + */ + +var Declaration = require('./declaration'); +var KIND = 'property'; + +/** + * Defines a class property + * @constructor Property + * @extends {Declaration} + * @property {boolean} isFinal + * @property {boolean} isStatic + * @property {string} visibility + * @property {Node|null} value + */ +var Property = Declaration.extends(function Property(name, value, flags, location) { + Declaration.apply(this, [KIND, name, location]); + this.value = value; + this.parseFlags(flags); +}); + +module.exports = Property; + +},{"./declaration":20}],66:[function(require,module,exports){ +/*! + * Copyright (C) 2017 Glayzzle (BSD3 License) + * @authors https://github.com/glayzzle/php-parser/graphs/contributors + * @url http://glayzzle.com + */ +"use strict"; +var Lookup = require('./lookup'); +var KIND = 'propertylookup'; + +/** + * Lookup to an object property + * @constructor PropertyLookup + * @extends {Lookup} + */ +var PropertyLookup = Lookup.extends(function PropertyLookup(what, offset, location) { + Lookup.apply(this, [KIND, what, offset, location]); +}); + +module.exports = PropertyLookup; + +},{"./lookup":48}],67:[function(require,module,exports){ +/*! + * Copyright (C) 2017 Glayzzle (BSD3 License) + * @authors https://github.com/glayzzle/php-parser/graphs/contributors + * @url http://glayzzle.com + */ +"use strict"; + +var Statement = require('./statement'); +var KIND = 'retif'; +var Bin = require('./bin'); +var PRECEDENCE = Bin.precedence[KIND]; + +/** + * Defines a short if statement that returns a value + * @constructor RetIf + * @extends {Statement} + * @property {Expression} test + * @property {Expression} trueExpr + * @property {Expression} falseExpr + */ +var RetIf = Statement.extends(function RetIf(test, trueExpr, falseExpr, location) { + Statement.apply(this, [KIND, location]); + this.test = test; + this.trueExpr = trueExpr; + this.falseExpr = falseExpr; +}); + +/** + * Handles precedence over items + */ +RetIf.prototype.precedence = function(node) { + var what = node.kind === 'bin' ? node.type : node.kind; + var lLevel = Bin.precedence[what]; + if (lLevel && PRECEDENCE < lLevel) { + if (node.kind === 'bin') { + node.right = this.test; + this.test = node; + return this; + } else { + throw new Error('@todo ' + node.kind); + } + } +}; + +module.exports = RetIf; + +},{"./bin":5,"./statement":70}],68:[function(require,module,exports){ +/*! + * Copyright (C) 2017 Glayzzle (BSD3 License) + * @authors https://github.com/glayzzle/php-parser/graphs/contributors + * @url http://glayzzle.com + */ +"use strict"; +var Node = require('./node'); +var KIND = 'return'; + +/** + * A continue statement + * @constructor Return + * @extends {Node} + * @property {Expression|null} expr + */ +var Return = Node.extends(function Return(expr, location) { + Node.apply(this, [KIND, location]); + this.expr = expr; +}); + +module.exports = Return; + +},{"./node":53}],69:[function(require,module,exports){ +/*! + * Copyright (C) 2017 Glayzzle (BSD3 License) + * @authors https://github.com/glayzzle/php-parser/graphs/contributors + * @url http://glayzzle.com + */ +"use strict"; + +var Statement = require('./statement'); +var KIND = 'silent'; + +/** + * Avoids to show/log warnings & notices from the inner expression + * @constructor Silent + * @extends {Statement} + * @property {Expression} expr + */ +var Silent = Statement.extends(function Silent(expr, location) { + Statement.apply(this, [KIND, location]); + this.expr = expr; +}); + +module.exports = Silent; + +},{"./statement":70}],70:[function(require,module,exports){ +/*! + * Copyright (C) 2017 Glayzzle (BSD3 License) + * @authors https://github.com/glayzzle/php-parser/graphs/contributors + * @url http://glayzzle.com + */ + +var Node = require('./node'); +var KIND = 'statement'; + +/** + * Any statement. + * @constructor Statement + * @extends {Node} + */ +var Statement = Node.extends(function Statement(kind, location) { + Node.apply(this, [kind || KIND, location]); +}); + +module.exports = Statement; + +},{"./node":53}],71:[function(require,module,exports){ +/*! + * Copyright (C) 2017 Glayzzle (BSD3 License) + * @authors https://github.com/glayzzle/php-parser/graphs/contributors + * @url http://glayzzle.com + */ +"use strict"; +var Statement = require('./statement'); +var KIND = 'static'; + +/** + * Declares a static variable into the current scope + * @constructor Static + * @extends {Statement} + * @property {Variable[]|Assign[]} items + */ +var Static = Statement.extends(function Static(items, location) { + Statement.apply(this, [KIND, location]); + this.items = items; +}); + +module.exports = Static; + +},{"./statement":70}],72:[function(require,module,exports){ +/*! + * Copyright (C) 2017 Glayzzle (BSD3 License) + * @authors https://github.com/glayzzle/php-parser/graphs/contributors + * @url http://glayzzle.com + */ +"use strict"; +var Lookup = require('./lookup'); +var KIND = 'staticlookup'; + +/** + * Lookup to a static property + * @constructor StaticLookup + * @extends {Lookup} + */ +var StaticLookup = Lookup.extends(function StaticLookup(what, offset, location) { + Lookup.apply(this, [KIND, what, offset, location]); +}); + +module.exports = StaticLookup; + +},{"./lookup":48}],73:[function(require,module,exports){ +/*! + * Copyright (C) 2017 Glayzzle (BSD3 License) + * @authors https://github.com/glayzzle/php-parser/graphs/contributors + * @url http://glayzzle.com + */ + +var Literal = require('./literal'); +var KIND = 'string'; + +/** + * Defines a string (simple ou double quoted) - chars are already escaped + * @constructor String + * @extends {Literal} + * @property {boolean} isDoubleQuote + * @see {Encapsed} + */ +var String = Literal.extends(function String(isDoubleQuote, value, location) { + Literal.apply(this, [KIND, value, location]); + this.isDoubleQuote = isDoubleQuote; +}); + +module.exports = String; + +},{"./literal":46}],74:[function(require,module,exports){ +/*! + * Copyright (C) 2017 Glayzzle (BSD3 License) + * @authors https://github.com/glayzzle/php-parser/graphs/contributors + * @url http://glayzzle.com + */ +"use strict"; + +var Statement = require('./statement'); +var KIND = 'switch'; + +/** + * Defines a switch statement + * @constructor Switch + * @extends {Statement} + * @property {Expression} test + * @property {Block} body + * @property {boolean} shortForm + */ +var Switch = Statement.extends(function Switch(test, body, shortForm, location) { + Statement.apply(this, [KIND, location]); + this.test = test; + this.body = body; + this.shortForm = shortForm; +}); + +module.exports = Switch; + +},{"./statement":70}],75:[function(require,module,exports){ +/*! + * Copyright (C) 2017 Glayzzle (BSD3 License) + * @authors https://github.com/glayzzle/php-parser/graphs/contributors + * @url http://glayzzle.com + */ + +var Statement = require('./statement'); +var KIND = 'sys'; + +/** + * Defines system based call + * @constructor Sys + * @extends {Statement} + * @property {Node[]} arguments + */ +var Sys = Statement.extends(function Sys(kind, args, location) { + Statement.apply(this, [kind || KIND, location]); + this.arguments = args; +}); + +module.exports = Sys; + +},{"./statement":70}],76:[function(require,module,exports){ +/*! + * Copyright (C) 2017 Glayzzle (BSD3 License) + * @authors https://github.com/glayzzle/php-parser/graphs/contributors + * @url http://glayzzle.com + */ +"use strict"; + +var Statement = require('./statement'); +var KIND = 'throw'; + +/** + * Defines a throw statement + * @constructor Throw + * @extends {Statement} + * @property {Expression} what + */ +var Throw = Statement.extends(function Throw(what, location) { + Statement.apply(this, [KIND, location]); + this.what = what; +}); + +module.exports = Throw; + +},{"./statement":70}],77:[function(require,module,exports){ +/*! + * Copyright (C) 2017 Glayzzle (BSD3 License) + * @authors https://github.com/glayzzle/php-parser/graphs/contributors + * @url http://glayzzle.com + */ + +var Declaration = require('./declaration'); +var KIND = 'trait'; + + +/** + * A trait definition + * @constructor Trait + * @extends {Declaration} + * @property {Identifier|null} extends + * @property {Identifier[]} implements + * @property {Declaration[]} body + */ +var Trait = Declaration.extends(function Trait(name, ext, impl, body, location) { + Declaration.apply(this, [KIND, name, location]); + this.extends = ext; + this.implements = impl; + this.body = body; +}); + +module.exports = Trait; + +},{"./declaration":20}],78:[function(require,module,exports){ +/*! + * Copyright (C) 2017 Glayzzle (BSD3 License) + * @authors https://github.com/glayzzle/php-parser/graphs/contributors + * @url http://glayzzle.com + */ + +var Node = require('./node'); +var KIND = 'traitalias'; + +var IS_PUBLIC = 'public'; +var IS_PROTECTED = 'protected'; +var IS_PRIVATE = 'private'; + +/** + * Defines a trait alias + * @constructor TraitAlias + * @extends {Node} + * @property {Identifier|null} trait + * @property {string} method + * @property {string|null} as + * @property {string|null} visibility + */ +var TraitAlias = Node.extends(function TraitAlias(trait, method, as, flags, location) { + Node.apply(this, [KIND, location]); + this.trait = trait; + this.method = method; + this.as = as; + if (flags) { + if (flags[0] === 0) { + this.visibility = IS_PUBLIC; + } else if (flags[0] === 1) { + this.visibility = IS_PROTECTED; + } else { + this.visibility = IS_PRIVATE; + } + } else { + this.visibility = null; + } +}); + +module.exports = TraitAlias; + +},{"./node":53}],79:[function(require,module,exports){ +/*! + * Copyright (C) 2017 Glayzzle (BSD3 License) + * @authors https://github.com/glayzzle/php-parser/graphs/contributors + * @url http://glayzzle.com + */ + +var Node = require('./node'); +var KIND = 'traitprecedence'; + +/** + * Defines a trait alias + * @constructor TraitPrecedence + * @extends {Node} + * @property {Identifier|null} trait + * @property {string} method + * @property {Identifier[]} instead + */ +var TraitPrecedence = Node.extends(function TraitPrecedence(trait, method, instead, location) { + Node.apply(this, [KIND, location]); + this.trait = trait; + this.method = method; + this.instead = instead; +}); + +module.exports = TraitPrecedence; + +},{"./node":53}],80:[function(require,module,exports){ +/*! + * Copyright (C) 2017 Glayzzle (BSD3 License) + * @authors https://github.com/glayzzle/php-parser/graphs/contributors + * @url http://glayzzle.com + */ + +var Node = require('./node'); +var KIND = 'traituse'; + +/** + * Defines a trait usage + * @constructor TraitUse + * @extends {Node} + * @property {Identifier[]} traits + * @property {Node[]|null} adaptations + */ +var TraitUse = Node.extends(function TraitUse(traits, adaptations, location) { + Node.apply(this, [KIND, location]); + this.traits = traits; + this.adaptations = adaptations; +}); + +module.exports = TraitUse; + +},{"./node":53}],81:[function(require,module,exports){ +/*! + * Copyright (C) 2017 Glayzzle (BSD3 License) + * @authors https://github.com/glayzzle/php-parser/graphs/contributors + * @url http://glayzzle.com + */ +"use strict"; + +var Statement = require('./statement'); +var KIND = 'try'; + +/** + * Defines a try statement + * @constructor Try + * @extends {Statement} + * @property {Block} body + * @property {Catch[]} catches + * @property {Block} allways + */ +var Try = Statement.extends(function Try(body, catches, always, location) { + Statement.apply(this, [KIND, location]); + this.body = body; + this.catches = catches; + this.always = always; +}); + +module.exports = Try; + +},{"./statement":70}],82:[function(require,module,exports){ +/*! + * Copyright (C) 2017 Glayzzle (BSD3 License) + * @authors https://github.com/glayzzle/php-parser/graphs/contributors + * @url http://glayzzle.com + */ +"use strict"; + +var Operation = require('./operation'); +var KIND = 'unary'; + +/** + * Unary operations + * @constructor Unary + * @extends {Operation} + * @property {String} type + * @property {Expression} what + */ +var Unary = Operation.extends(function Unary(type, what, location) { + Operation.apply(this, [KIND, location]); + this.type = type; + this.what = what; +}); + +Unary.prototype.precedence = function(node) { + if (node.kind === 'bin') { + this.what = node.left; + node.left = this; + return node; + } else if (node.kind === 'retif') { + this.what = node.test; + node.test = this; + return node; + } +}; + +module.exports = Unary; + +},{"./operation":57}],83:[function(require,module,exports){ +/*! + * Copyright (C) 2017 Glayzzle (BSD3 License) + * @authors https://github.com/glayzzle/php-parser/graphs/contributors + * @url http://glayzzle.com + */ + +var Sys = require('./sys'); +var KIND = 'unset'; + +/** + * Deletes references to a list of variables + * @constructor Unset + * @extends {Sys} + */ +var Unset = Sys.extends(function Unset(args, location) { + Sys.apply(this, [KIND, args, location]); +}); + +module.exports = Unset; + +},{"./sys":75}],84:[function(require,module,exports){ +/*! + * Copyright (C) 2017 Glayzzle (BSD3 License) + * @authors https://github.com/glayzzle/php-parser/graphs/contributors + * @url http://glayzzle.com + */ +"use strict"; +var Statement = require('./statement'); +var KIND = 'usegroup'; + +/** + * Defines a use statement (with a list of use items) + * @constructor UseGroup + * @extends {Statement} + * @property {String|null} name + * @property {String|null} type - Possible value : function, const + * @property {UseItem[]} item + * @see {Namespace} + * @see http://php.net/manual/en/language.namespaces.importing.php + */ +var UseGroup = Statement.extends(function UseGroup(name, type, items, location) { + Statement.apply(this, [KIND, location]); + this.name = name; + this.type = type; + this.items = items; +}); + +module.exports = UseGroup; + +},{"./statement":70}],85:[function(require,module,exports){ +/*! + * Copyright (C) 2017 Glayzzle (BSD3 License) + * @authors https://github.com/glayzzle/php-parser/graphs/contributors + * @url http://glayzzle.com + */ +"use strict"; +var Statement = require('./statement'); +var KIND = 'useitem'; + +/** + * Defines a use statement (from namespace) + * @constructor UseItem + * @extends {Statement} + * @property {String} name + * @property {String|null} type - Possible value : function, const + * @property {String|null} alias + * @see {Namespace} + * @see http://php.net/manual/en/language.namespaces.importing.php + */ +var UseItem = Statement.extends(function UseItem(name, alias, type, location) { + Statement.apply(this, [KIND, location]); + this.name = name; + this.alias = alias; + this.type = type; +}); + + +/** + * Importing a constant + * @constant {String} TYPE_CONST + */ +UseItem.TYPE_CONST = 'const'; +/** + * Importing a function + * @constant {String} TYPE_FUNC + */ +UseItem.TYPE_FUNCTION = 'function'; + + +module.exports = UseItem; + +},{"./statement":70}],86:[function(require,module,exports){ +/*! + * Copyright (C) 2017 Glayzzle (BSD3 License) + * @authors https://github.com/glayzzle/php-parser/graphs/contributors + * @url http://glayzzle.com + */ +"use strict"; +var Expr = require('./expression'); +var KIND = 'variable'; + +/** + * Any expression node. Since the left-hand side of an assignment may + * be any expression in general, an expression can also be a pattern. + * @constructor Variable + * @extends {Expression} + * @property {String|Node} name + * @property {boolean} byref + */ +var Variable = Expr.extends(function Variable(name, byref, location) { + Expr.apply(this, [KIND, location]); + this.name = name; + this.byref = byref || false; +}); + +module.exports = Variable; + +},{"./expression":31}],87:[function(require,module,exports){ +/*! + * Copyright (C) 2017 Glayzzle (BSD3 License) + * @authors https://github.com/glayzzle/php-parser/graphs/contributors + * @url http://glayzzle.com + */ +"use strict"; +var Expr = require('./expression'); +var KIND = 'variadic'; + +/** + * Introduce a list of items into the arguments of the call + * @constructor Variadic + * @extends {Expression} + * @property {Array|Expression} what + * @see https://wiki.php.net/rfc/argument_unpacking + */ +var Variadic = Expr.extends(function Variadic(what, location) { + Expr.apply(this, [KIND, location]); + this.what = what; +}); + +module.exports = Variadic; + +},{"./expression":31}],88:[function(require,module,exports){ +/*! + * Copyright (C) 2017 Glayzzle (BSD3 License) + * @authors https://github.com/glayzzle/php-parser/graphs/contributors + * @url http://glayzzle.com + */ +"use strict"; + +var Statement = require('./statement'); +var KIND = 'while'; + +/** + * Defines a while statement + * @constructor While + * @extends {Statement} + * @property {Expression} test + * @property {Statement} body + * @property {boolean} shortForm + */ +var While = Statement.extends(function While(test, body, shortForm, location) { + Statement.apply(this, [KIND, location]); + this.test = test; + this.body = body; + this.shortForm = shortForm; +}); + +module.exports = While; + +},{"./statement":70}],89:[function(require,module,exports){ +/*! + * Copyright (C) 2017 Glayzzle (BSD3 License) + * @authors https://github.com/glayzzle/php-parser/graphs/contributors + * @url http://glayzzle.com + */ +"use strict"; + +var Expression = require('./expression'); +var KIND = 'yield'; + +/** + * Defines a yield generator statement + * @constructor Yield + * @extends {Expression} + * @property {Expression|Null} value + * @property {Expression|Null} key + * @see http://php.net/manual/en/language.generators.syntax.php + */ +var Yield = Expression.extends(function Yield(value, key, location) { + Expression.apply(this, [KIND, location]); + this.value = value; + this.key = key; +}); + +module.exports = Yield; + +},{"./expression":31}],90:[function(require,module,exports){ +/*! + * Copyright (C) 2017 Glayzzle (BSD3 License) + * @authors https://github.com/glayzzle/php-parser/graphs/contributors + * @url http://glayzzle.com + */ +"use strict"; + +var Expression = require('./expression'); +var KIND = 'yieldfrom'; + +/** + * Defines a yield from generator statement + * @constructor YieldFrom + * @extends {Expression} + * @property {Expression} value + * @see http://php.net/manual/en/language.generators.syntax.php + */ +var YieldFrom = Expression.extends(function YieldFrom(value, location) { + Expression.apply(this, [KIND, location]); + this.value = value; +}); + +module.exports = YieldFrom; + +},{"./expression":31}],91:[function(require,module,exports){ +/*! + * Copyright (C) 2017 Glayzzle (BSD3 License) + * @authors https://github.com/glayzzle/php-parser/graphs/contributors + * @url http://glayzzle.com + */ + +var lexer = require('./lexer'); +var parser = require('./parser'); +var tokens = require('./tokens'); +var AST = require('./ast'); + +/** + * @private combine structures + */ +function combine(src, to) { + var keys = Object.keys(src); + var i = keys.length; + while (i--) { + var k = keys[i]; + var val = src[k]; + if (val === null) { + delete to[k]; + } else if (typeof val === 'function') { + to[k] = val.bind(to); + } else if (Array.isArray(val)) { + to[k] = Array.isArray(to[k]) ? to[k].concat(val) : val; + } else if (typeof val === 'object') { + to[k] = typeof to[k] === 'object' ? combine(val, to[k]) : val; + } else { + to[k] = val; + } + } + return to; +} + +/** + * Initialise a new parser instance with the specified options + * + * Usage : + * ```js + * var parser = require('php-parser'); + * var instance = new parser({ + * parser: { + * extractDoc: true, + * suppressErrors: true + * }, + * ast: { + * withPositions: true + * }, + * lexer: { + * short_tags: true, + * asp_tags: true + * } + * }); + * + * var evalAST = instance.parseEval('some php code'); + * var codeAST = instance.parseCode(' Note that the output tokens are *STRICLY* similar to PHP function `token_get_all` + * @param {String} buffer + * @return {String[]} - Each item can be a string or an array with following informations [token_name, text, line_number] + */ +engine.prototype.tokenGetAll = function(buffer) { + this.lexer.mode_eval = false; + this.lexer.all_tokens = true; + var EOF = this.lexer.EOF; + var names = this.tokens.values; + this.lexer.setInput(buffer); + var token = this.lexer.lex() || EOF; + var result = []; + while(token != EOF) { + var entry = this.lexer.yytext; + if (names.hasOwnProperty(token)) { + entry = [names[token], entry, this.lexer.yylloc.first_line]; + } + result.push(entry); + token = this.lexer.lex() || EOF; + } + return result; +}; + +// exports the function +module.exports = engine; + +},{"./ast":2,"./lexer":92,"./parser":101,"./tokens":117}],92:[function(require,module,exports){ +/*! + * Copyright (C) 2017 Glayzzle (BSD3 License) + * @authors https://github.com/glayzzle/php-parser/graphs/contributors + * @url http://glayzzle.com + */ +"use strict"; +/** + * This is the php lexer. It will tokenize the string for helping the + * parser to build the AST from its grammar. + * + * @public @constructor {Lexer} + * @property {Integer} EOF + * @property {Boolean} all_tokens defines if all tokens must be retrieved (used by token_get_all only) + * @property {Boolean} comment_tokens extracts comments tokens + * @property {Boolean} mode_eval enables the evald mode (ignore opening tags) + * @property {Boolean} asp_tags disables by default asp tags mode + * @property {Boolean} short_tags enables by default short tags mode + * @property {Object} keywords List of php keyword + * @property {Object} castKeywords List of php keywords for type casting + */ +var lexer = function(engine) { + this.engine = engine; + this.tok = this.engine.tokens.names; + this.EOF = 1; + this.debug = false; + this.all_tokens = true; + this.comment_tokens = false; + this.mode_eval = false; + this.asp_tags = false; + this.short_tags = true; + this.yyprevcol = 0; + this.keywords = { + "__class__": this.tok.T_CLASS_C, + "__trait__": this.tok.T_TRAIT_C, + "__function__": this.tok.T_FUNC_C, + "__method__": this.tok.T_METHOD_C, + "__line__": this.tok.T_LINE, + "__file__": this.tok.T_FILE, + "__dir__": this.tok.T_DIR, + "__namespace__": this.tok.T_NS_C, + 'exit': this.tok.T_EXIT, + 'die': this.tok.T_EXIT, + 'function': this.tok.T_FUNCTION, + "const": this.tok.T_CONST, + "return": this.tok.T_RETURN, + "try": this.tok.T_TRY, + "catch": this.tok.T_CATCH, + "finally": this.tok.T_FINALLY, + "throw": this.tok.T_THROW, + "if": this.tok.T_IF, + "elseif": this.tok.T_ELSEIF, + "endif": this.tok.T_ENDIF, + "else": this.tok.T_ELSE, + "while": this.tok.T_WHILE, + "endwhile": this.tok.T_ENDWHILE, + "do": this.tok.T_DO, + "for": this.tok.T_FOR, + "endfor": this.tok.T_ENDFOR, + "foreach": this.tok.T_FOREACH, + "endforeach": this.tok.T_ENDFOREACH, + "declare": this.tok.T_DECLARE, + "enddeclare": this.tok.T_ENDDECLARE, + "instanceof": this.tok.T_INSTANCEOF, + "as": this.tok.T_AS, + "switch": this.tok.T_SWITCH, + "endswitch": this.tok.T_ENDSWITCH, + "case": this.tok.T_CASE, + "default": this.tok.T_DEFAULT, + "break": this.tok.T_BREAK, + "continue": this.tok.T_CONTINUE, + "goto": this.tok.T_GOTO, + "echo": this.tok.T_ECHO, + "print": this.tok.T_PRINT, + "class": this.tok.T_CLASS, + "interface": this.tok.T_INTERFACE, + "trait": this.tok.T_TRAIT, + "extends": this.tok.T_EXTENDS, + "implements": this.tok.T_IMPLEMENTS, + "new": this.tok.T_NEW, + "clone": this.tok.T_CLONE, + "var": this.tok.T_VAR, + "eval": this.tok.T_EVAL, + "include": this.tok.T_INCLUDE, + "include_once": this.tok.T_INCLUDE_ONCE, + "require": this.tok.T_REQUIRE, + "require_once": this.tok.T_REQUIRE_ONCE, + "namespace": this.tok.T_NAMESPACE, + "use": this.tok.T_USE, + "insteadof": this.tok.T_INSTEADOF, + "global": this.tok.T_GLOBAL, + "isset": this.tok.T_ISSET, + "empty": this.tok.T_EMPTY, + "__halt_compiler": this.tok.T_HALT_COMPILER, + "static": this.tok.T_STATIC, + "abstract": this.tok.T_ABSTRACT, + "final": this.tok.T_FINAL, + "private": this.tok.T_PRIVATE, + "protected": this.tok.T_PROTECTED, + "public": this.tok.T_PUBLIC, + "unset": this.tok.T_UNSET, + "list": this.tok.T_LIST, + "array": this.tok.T_ARRAY, + "callable": this.tok.T_CALLABLE, + "or": this.tok.T_LOGICAL_OR, + "and": this.tok.T_LOGICAL_AND, + "xor": this.tok.T_LOGICAL_XOR + }; + this.castKeywords = { + 'int': this.tok.T_INT_CAST, + 'integer': this.tok.T_INT_CAST, + "real": this.tok.T_DOUBLE_CAST, + "double": this.tok.T_DOUBLE_CAST, + "float": this.tok.T_DOUBLE_CAST, + "string": this.tok.T_STRING_CAST, + "binary": this.tok.T_STRING_CAST, + "array": this.tok.T_ARRAY_CAST, + "object": this.tok.T_OBJECT_CAST, + "bool": this.tok.T_BOOL_CAST, + "boolean": this.tok.T_BOOL_CAST, + "unset": this.tok.T_UNSET_CAST + }; +}; + +/** + * Initialize the lexer with the specified input + */ +lexer.prototype.setInput = function(input) { + this._input = input; + this.size = input.length; + this.yylineno = 1; + this.offset = 0; + this.yyprevcol = 0; + this.yytext = ''; + this.yylloc = { + first_offset: 0, + first_line: 1, + first_column: 0, + prev_offset: 0, + prev_line: 1, + prev_column: 0, + last_line: 1, + last_column: 0 + }; + this.tokens = []; + this.conditionStack = []; + this.done = this.offset >= this.size; + if (!this.all_tokens && this.mode_eval) { + this.begin('ST_IN_SCRIPTING'); + } else { + this.begin('INITIAL'); + } + return this; +}; + + +/** + * consumes and returns one char from the input + */ +lexer.prototype.input = function(size) { + var ch = this._input[this.offset]; + if (!ch) return ''; + this.yytext += ch; + this.offset ++; + if ( ch === '\r' && this._input[this.offset] === '\n' ) { + this.yytext += '\n'; + this.offset++; + } + if (ch === '\n' || ch === '\r') { + this.yylloc.last_line = ++this.yylineno; + this.yyprevcol = this.yylloc.last_column; + this.yylloc.last_column = 0; + } else { + this.yylloc.last_column++; + } + return ch; +}; + +/** + * revert eating specified size + */ +lexer.prototype.unput = function(size) { + if (size === 1) { + // 1 char unput (most cases) + this.offset --; + if (this._input[this.offset] === '\n' && this._input[this.offset - 1] === '\r') { + this.offset --; + size ++; + } + if (this._input[this.offset] === '\r' || this._input[this.offset] === '\n') { + this.yylloc.last_line --; + this.yylineno --; + this.yylloc.last_column = this.yyprevcol; + } else { + this.yylloc.last_column --; + } + this.yytext = this.yytext.substring(0, this.yytext.length - size); + } else if (size > 0) { + this.offset -= size; + if (size < this.yytext.length) { + this.yytext = this.yytext.substring(0, this.yytext.length - size); + // re-calculate position + this.yylloc.last_line = this.yylloc.first_line; + this.yylloc.last_column = this.yyprevcol = this.yylloc.first_column; + for(var i = 0; i < this.yytext.length; i++) { + var c = this.yytext[i]; + if (c === '\r') { + c = this.yytext[++i]; + this.yyprevcol = this.yylloc.last_column; + this.yylloc.last_line ++; + this.yylloc.last_column = 0; + if (c !== '\n') { + if (c === '\r') { + this.yylloc.last_line ++; + } else { + this.yylloc.last_column ++; + } + } + } else if (c === '\n') { + this.yyprevcol = this.yylloc.last_column; + this.yylloc.last_line ++; + this.yylloc.last_column = 0; + } else { + this.yylloc.last_column ++; + } + } + this.yylineno = this.yylloc.last_line; + } else { + // reset full text + this.yytext = ""; + this.yylloc.last_line = this.yylineno = this.yylloc.first_line; + this.yylloc.last_column = this.yylloc.first_column; + } + } + + return this; +}; + +// check if the text matches +lexer.prototype.tryMatch = function(text) { + return text === this.ahead(text.length); +}; + +// check if the text matches +lexer.prototype.tryMatchCaseless = function(text) { + return text === this.ahead(text.length).toLowerCase(); +}; + +// look ahead +lexer.prototype.ahead = function(size) { + var text = this._input.substring(this.offset, this.offset + size); + if (text[text.length - 1] === '\r' && this._input[this.offset + size + 1] === '\n') { + text += '\n'; + } + return text; +}; + +// consume the specified size +lexer.prototype.consume = function(size) { + for(var i = 0; i < size; i++) { + var ch = this._input[this.offset]; + if (!ch) break; + this.yytext += ch; + this.offset ++; + if ( ch === '\r' && this._input[this.offset] === '\n' ) { + this.yytext += '\n'; + this.offset++; + i++; + } + if (ch === '\n' || ch === '\r') { + this.yylloc.last_line = ++this.yylineno; + this.yyprevcol = this.yylloc.last_column; + this.yylloc.last_column = 0; + } else { + this.yylloc.last_column++; + } + } + return this; +}; + +/** + * Gets the current state + */ +lexer.prototype.getState = function() { + return { + yytext: this.yytext, + offset: this.offset, + yylineno: this.yylineno, + yyprevcol: this.yyprevcol, + yylloc: { + first_offset: this.yylloc.first_offset, + first_line: this.yylloc.first_line, + first_column: this.yylloc.first_column, + last_line: this.yylloc.last_line, + last_column: this.yylloc.last_column + } + }; +}; + +/** + * Sets the current lexer state + */ +lexer.prototype.setState = function(state) { + this.yytext = state.yytext; + this.offset = state.offset; + this.yylineno = state.yylineno; + this.yyprevcol = state.yyprevcol; + this.yylloc = state.yylloc; + return this; +}; + +// prepend next token +lexer.prototype.appendToken = function(value, ahead) { + this.tokens.push([value, ahead]); + return this; +}; + +// return next match that has a token +lexer.prototype.lex = function() { + this.yylloc.prev_offset = this.offset; + this.yylloc.prev_line = this.yylloc.last_line; + this.yylloc.prev_column = this.yylloc.last_column; + var token = this.next() || this.lex(); + if (!this.all_tokens) { + while( + token === this.tok.T_WHITESPACE // ignore white space + || ( + !this.comment_tokens && ( + token === this.tok.T_COMMENT // ignore single lines comments + || token === this.tok.T_DOC_COMMENT // ignore doc comments + ) + ) + || ( + // ignore open tags + token === this.tok.T_OPEN_TAG + ) + ) { + token = this.next() || this.lex(); + } + if (!this.mode_eval && token == this.tok.T_OPEN_TAG_WITH_ECHO) { + // open tag with echo statement + return this.tok.T_ECHO; + } + } + if (!this.yylloc.prev_offset) { + this.yylloc.prev_offset = this.yylloc.first_offset; + this.yylloc.prev_line = this.yylloc.first_line; + this.yylloc.prev_column = this.yylloc.first_column; + } + /*else if (this.yylloc.prev_offset === this.offset && this.offset !== this.size) { + throw new Error('Infinite loop @ ' + this.offset + ' / ' + this.size); + }*/ + return token; +}; + +// activates a new lexer condition state (pushes the new lexer condition state onto the condition stack) +lexer.prototype.begin = function(condition) { + this.conditionStack.push(condition); + this.curCondition = condition; + this.stateCb = this['match' + condition]; + if (typeof this.stateCb !== 'function') { + throw new Error('Undefined condition state "'+condition+'"'); + } + return this; +}; + +// pop the previously active lexer condition state off the condition stack +lexer.prototype.popState = function() { + var n = this.conditionStack.length - 1; + var condition = (n > 0) ? this.conditionStack.pop() : this.conditionStack[0]; + this.curCondition = this.conditionStack[this.conditionStack.length - 1]; + this.stateCb = this['match' + this.curCondition]; + if (typeof this.stateCb !== 'function') { + throw new Error('Undefined condition state "'+this.curCondition+'"'); + } + return condition; +}; + +// return next match in input +lexer.prototype.next = function () { + var token; + if (!this._input) { + this.done = true; + } + this.yylloc.first_offset = this.offset; + this.yylloc.first_line = this.yylloc.last_line; + this.yylloc.first_column = this.yylloc.last_column; + this.yytext = ''; + if (this.done) { + this.yylloc.prev_offset = this.yylloc.first_offset; + this.yylloc.prev_line = this.yylloc.first_line; + this.yylloc.prev_column = this.yylloc.first_column; + return this.EOF; + } + if (this.tokens.length > 0) { + token = this.tokens.shift(); + if (typeof token[1] === 'object') { + this.setState(token[1]); + } else { + this.consume(token[1]); + } + token = token[0]; + } else { + token = this.stateCb.apply(this, []); + } + if (this.offset >= this.size && this.tokens.length === 0) { + this.done = true; + } + if (this.debug) { + var tName = token; + if (typeof tName === 'number') { + tName = this.engine.tokens.values[tName]; + } else { + tName = '"'+tName+'"'; + } + var e = new Error( + tName + + '\tfrom ' + this.yylloc.first_line + ',' + this.yylloc.first_column + + '\t - to ' + this.yylloc.last_line + ',' + this.yylloc.last_column + + '\t"'+this.yytext+'"' + ); + console.log(e.stack); + } + return token; +}; + + +// extends the lexer with states +[ + require('./lexer/comments.js'), + require('./lexer/initial.js'), + require('./lexer/numbers.js'), + require('./lexer/property.js'), + require('./lexer/scripting.js'), + require('./lexer/strings.js'), + require('./lexer/tokens.js'), + require('./lexer/utils.js') +].forEach(function (ext) { + for(var k in ext) { + lexer.prototype[k] = ext[k]; + } +}); + +module.exports = lexer; + +},{"./lexer/comments.js":93,"./lexer/initial.js":94,"./lexer/numbers.js":95,"./lexer/property.js":96,"./lexer/scripting.js":97,"./lexer/strings.js":98,"./lexer/tokens.js":99,"./lexer/utils.js":100}],93:[function(require,module,exports){ +/*! + * Copyright (C) 2017 Glayzzle (BSD3 License) + * @authors https://github.com/glayzzle/php-parser/graphs/contributors + * @url http://glayzzle.com + */ + +"use strict"; + +module.exports = { + /** + * Reads a single line comment + * @see + */ + T_COMMENT: function() { + while(this.offset < this.size) { + var ch = this.input(); + if (ch === '\n' || ch === '\r') { + return this.tok.T_COMMENT; + } else if (ch === '?' && !this.aspTagMode && this._input[this.offset] === '>') { + this.unput(1); + return this.tok.T_COMMENT; + } else if (ch === '%' && this.aspTagMode && this._input[this.offset] === '>') { + this.unput(1); + return this.tok.T_COMMENT; + } + } + return this.tok.T_COMMENT; + }, + /** + * Behaviour : https://github.com/php/php-src/blob/master/Zend/zend_language_scanner.l#L1927 + */ + T_DOC_COMMENT: function() { + var ch = this.input(); + var token = this.tok.T_COMMENT; + if (ch === '*') { // started with '/*' , check is next is '*' + ch = this.input(); + if (this.is_WHITESPACE()) { // check if next is WHITESPACE + token = this.tok.T_DOC_COMMENT; + } + if (ch === '/') { + return token; + } else { + this.unput(1); // reset + } + } + while(this.offset < this.size) { + ch = this.input(); + if (ch === '*' && this._input[this.offset] === '/') { + this.input(); + break; + } + } + return token; + } +}; + +},{}],94:[function(require,module,exports){ +/*! + * Copyright (C) 2017 Glayzzle (BSD3 License) + * @authors https://github.com/glayzzle/php-parser/graphs/contributors + * @url http://glayzzle.com + */ +module.exports = { + nextINITIAL: function() { + if ( + this.conditionStack.length > 1 + && this.conditionStack[this.conditionStack.length - 1] === 'INITIAL' + ) { + // Return to HEREDOC/ST_DOUBLE_QUOTES mode + this.popState(); + } else { + this.begin("ST_IN_SCRIPTING"); + } + return this; + }, + matchINITIAL: function() { + while(this.offset < this.size) { + var ch = this.input(); + if (ch == '<') { + ch = this.ahead(1); + if (ch == '?') { + if (this.tryMatch('?=')) { + this.unput(1).appendToken(this.tok.T_OPEN_TAG_WITH_ECHO, 3).nextINITIAL(); + break; + } else if (this.tryMatchCaseless('?php')) { + ch = this._input[this.offset + 4]; + if (ch === ' ' || ch === '\t' || ch === '\n' || ch === '\r') { + this.unput(1).appendToken(this.tok.T_OPEN_TAG, 6).nextINITIAL(); + break; + } + } + if (this.short_tags) { + this.unput(1).appendToken(this.tok.T_OPEN_TAG, 2).nextINITIAL(); + break; + } + } else if(this.asp_tags && ch == '%') { + if (this.tryMatch('%=')) { + this.aspTagMode = true; + this.unput(1).appendToken(this.tok.T_OPEN_TAG_WITH_ECHO, 3).nextINITIAL(); + break; + } else { + this.aspTagMode = true; + this.unput(1).appendToken(this.tok.T_OPEN_TAG, 2).nextINITIAL(); + break; + } + } + } + } + if (this.yytext.length > 0) { + return this.tok.T_INLINE_HTML; + } else { + return false; + } + } +}; + +},{}],95:[function(require,module,exports){ +(function (process){ +/*! + * Copyright (C) 2017 Glayzzle (BSD3 License) + * @authors https://github.com/glayzzle/php-parser/graphs/contributors + * @url http://glayzzle.com + */ + +"use strict"; + +/* istanbul ignore else */ +if (process.arch == 'x64') { + var SIZEOF_LONG = 8; + var MAX_LENGTH_OF_LONG = 19; + var long_min_digits = "9223372036854775808"; +} else { + var SIZEOF_LONG = 4; + var MAX_LENGTH_OF_LONG = 10; + var long_min_digits = "2147483648"; +} + +module.exports = { + consume_NUM: function() { + var ch = this.yytext[0]; + var hasPoint = this.yytext[0] === '.'; + if (ch === '0') { + ch = this.input(); + // check if hexa + if (ch === 'x' || ch === 'X') { + this.input(); + if (this.is_HEX()) { + return this.consume_HNUM(); + } else { + this.unput(2); + } + } else if (ch === 'b' || ch === 'B') { + ch = this.input(); + if (ch === '0' || ch === '1') { + return this.consume_BNUM(); + } else { + this.unput(2); + } + } else if (!this.is_NUM()) { + this.unput(1); + } + } + + while(this.offset < this.size) { + ch = this.input(); + if (!this.is_NUM()) { + if (ch === '.' && !hasPoint) { + hasPoint = true; + } else if (ch === 'e' || ch === 'E') { + ch = this.input(); + if (ch === '+' || ch === '-') { + ch = this.input(); + if (this.is_NUM()) { + this.consume_LNUM(); + return this.tok.T_DNUMBER; + } else { + if (ch) this.unput(3); + break; + } + } else if (this.is_NUM()) { + this.consume_LNUM(); + return this.tok.T_DNUMBER; + } else { + if (ch) this.unput(2); + break; + } + } else { + this.unput(1); + break; + } + } + } + if (hasPoint) { + return this.tok.T_DNUMBER; + } else if (this.yytext.length < MAX_LENGTH_OF_LONG - 1) { + return this.tok.T_LNUMBER; + } else { + if ( + this.yytext.length < MAX_LENGTH_OF_LONG || ( + this.yytext.length == MAX_LENGTH_OF_LONG + && this.yytext < long_min_digits + ) + ) { + return this.tok.T_LNUMBER; + } + return this.tok.T_DNUMBER; + } + }, + // read hexa + consume_HNUM: function() { + while(this.offset < this.size) { + this.input(); + if (!this.is_HEX()) { + this.unput(1); + break; + } + } + return this.tok.T_LNUMBER; + }, + // read a generic number + consume_LNUM: function() { + while(this.offset < this.size) { + this.input(); + if (!this.is_NUM()) { + this.unput(1); + break; + } + } + return this.tok.T_LNUMBER; + }, + // read binary + consume_BNUM: function() { + var ch; + while(this.offset < this.size) { + ch = this.input(); + if (ch !== '0' && ch !== '1') { + if (ch) this.unput(1); + break; + } + } + return this.tok.T_LNUMBER; + } +}; + +}).call(this,require('_process')) +},{"_process":1}],96:[function(require,module,exports){ +/*! + * Copyright (C) 2017 Glayzzle (BSD3 License) + * @authors https://github.com/glayzzle/php-parser/graphs/contributors + * @url http://glayzzle.com + */ +module.exports = { + matchST_LOOKING_FOR_PROPERTY: function() { + var ch = this.input(); + if (ch === '-') { + ch = this.input(); + if (ch === '>') { + // https://github.com/php/php-src/blob/master/Zend/zend_language_scanner.l#L1296 + return this.tok.T_OBJECT_OPERATOR; + } + if (ch) this.unput(1); + } else if (this.is_LABEL_START()) { + // https://github.com/php/php-src/blob/master/Zend/zend_language_scanner.l#L1300 + this.consume_LABEL(); + this.popState(); + return this.tok.T_STRING; + } + // https://github.com/php/php-src/blob/master/Zend/zend_language_scanner.l#L1306 + this.popState(); + if (ch) this.unput(1); + return false; + }, + matchST_LOOKING_FOR_VARNAME: function() { + var ch = this.input(); + if (this.is_LABEL_START()) { + this.consume_LABEL(); + ch = this.input(); + this.popState(); + if (ch === '[' || ch === '}') { + this.begin('ST_IN_SCRIPTING'); + this.unput(1); + return this.tok.T_STRING_VARNAME; + } else { + this.unput(this.yytext.length); + return false; + } + } else { + if (ch) this.unput(1); + this.popState(); + this.begin('ST_IN_SCRIPTING'); + // console.log(this.yylineno, 'ST_LOOKING_FOR_VARNAME', this._input[this.offset - 1], this.conditionStack); + return false; + } + }, + matchST_VAR_OFFSET: function() { + var ch = this.input(); + if (this.is_NUM()) { + this.consume_NUM(); + return this.tok.T_NUM_STRING; + } else if (ch === ']') { + this.popState(); + return ']'; + } else if (ch === '$') { + this.input(); + if (this.is_LABEL_START()) { + this.consume_LABEL(); + return this.tok.T_VARIABLE; + } else { + throw new Error('Unexpected terminal'); + } + } else if (this.is_LABEL_START()) { + this.consume_LABEL(); + return this.tok.T_STRING; + } else if (this.is_WHITESPACE() || ch === '\\' || ch === '\'' || ch === '#') { + return this.tok.T_ENCAPSED_AND_WHITESPACE; + } else if (ch === '[' || ch === '{' || ch === '}' || ch === '"' || ch === '`' || this.is_TOKEN()) { + return ch; + } else { + throw new Error('Unexpected terminal'); + } + } +}; + +},{}],97:[function(require,module,exports){ +/*! + * Copyright (C) 2017 Glayzzle (BSD3 License) + * @authors https://github.com/glayzzle/php-parser/graphs/contributors + * @url http://glayzzle.com + */ +module.exports = { + matchST_IN_SCRIPTING: function() { + var ch = this.input(); + switch(ch) { + case ' ': + case '\t': + case '\n': + case '\r': + case '\r\n': + return this.T_WHITESPACE(); + case '#': + return this.T_COMMENT(); + case '/': + if (this._input[this.offset] === '/') { + return this.T_COMMENT(); + } else if (this._input[this.offset] === '*') { + this.input(); + return this.T_DOC_COMMENT(); + } + return this.consume_TOKEN(); + case '\'': + return this.T_CONSTANT_ENCAPSED_STRING(); + case '"': + return this.ST_DOUBLE_QUOTES(); + case '`': + this.begin('ST_BACKQUOTE'); + return '`'; + case '?': + if (!this.aspTagMode && this.tryMatch('>')) { + this.input(); + var nextCH = this._input[this.offset]; + if (nextCH === '\n' || nextCH === '\r') this.input(); + if (this.conditionStack.length > 1) { + this.begin('INITIAL'); + } + return this.tok.T_CLOSE_TAG; + } + return this.consume_TOKEN(); + case '%': + if (this.aspTagMode && this._input[this.offset] === '>') { + this.input(); // consume the '>' + ch = this._input[this.offset]; // read next + if (ch === '\n' || ch === '\r') { + this.input(); // consume the newline + } + this.aspTagMode = false; + if (this.conditionStack.length > 1) { + this.begin('INITIAL'); + } + return this.tok.T_CLOSE_TAG; + } + return this.consume_TOKEN(); + case '{': + this.begin('ST_IN_SCRIPTING'); + return '{'; + case '}': + if (this.conditionStack.length > 2) { + // Return to HEREDOC/ST_DOUBLE_QUOTES mode + this.popState(); + } + return '}'; + default: + if (ch === '.') { + ch = this.input(); + if (this.is_NUM()) { + return this.consume_NUM(); + } else { + if (ch) this.unput(1); + } + } + if (this.is_NUM()) { + return this.consume_NUM(); + } else if (this.is_LABEL_START()) { + return this.consume_LABEL().T_STRING(); + } else if(this.is_TOKEN()) { + return this.consume_TOKEN(); + } + } + throw new Error( + 'Bad terminal sequence "' + ch + '" at line ' + this.yylineno + ' (offset ' + this.offset + ')' + ); + }, + + T_WHITESPACE: function() { + while(this.offset < this.size) { + var ch = this.input(); + if (ch === ' ' || ch === '\t' || ch === '\n' || ch === '\r') { + continue; + } + this.unput(1); + break; + } + return this.tok.T_WHITESPACE; + } +}; + +},{}],98:[function(require,module,exports){ +/*! + * Copyright (C) 2017 Glayzzle (BSD3 License) + * @authors https://github.com/glayzzle/php-parser/graphs/contributors + * @url http://glayzzle.com + */ +module.exports = { + T_CONSTANT_ENCAPSED_STRING: function() { + var ch; + while(this.offset < this.size) { + ch = this.input(); + if (ch == '\\') { + this.input(); + } else if (ch == '\'') { + break; + } + } + return this.tok.T_CONSTANT_ENCAPSED_STRING; + }, + // check if matching a HEREDOC state + is_HEREDOC: function() { + var revert = this.offset; + if ( + this._input[this.offset - 1] === '<' + && this._input[this.offset] === '<' + && this._input[this.offset + 1] === '<' + ) { + this.offset += 3; + + // optional tabs / spaces + if (this.is_TABSPACE()) { + while(this.offset < this.size) { + this.offset ++; + if (!this.is_TABSPACE()) { + break; + } + } + } + + // optional quotes + var tChar = this._input[this.offset - 1]; + if (tChar === '\'' || tChar === '"') { + this.offset ++; + } else { + tChar = null; + } + + // required label + if (this.is_LABEL_START()) { + var yyoffset = this.offset - 1; + while(this.offset < this.size) { + this.offset++; + if (!this.is_LABEL()) { + break; + } + } + var yylabel = this._input.substring(yyoffset, this.offset - 1); + if (!tChar || tChar === this._input[this.offset - 1]) { // required ending quote + if (tChar) this.offset ++; + // require newline + if (this._input[this.offset - 1] === '\r' || this._input[this.offset - 1] === '\n') { + // go go go + this.heredoc_label = yylabel; + yyoffset = this.offset - revert; + this.offset = revert; + this.consume(yyoffset); + if (tChar === '\'') { + this.begin('ST_NOWDOC'); + } else { + this.begin('ST_HEREDOC'); + } + return this.tok.T_START_HEREDOC; + } + } + } + } + this.offset = revert; + return false; + }, + ST_DOUBLE_QUOTES: function() { + var ch; + while(this.offset < this.size) { + ch = this.input(); + if (ch == '\\') { + this.input(); + } else if (ch == '"') { + break; + } else if (ch == '$') { + ch = this.input(); + if ( ch == '{' || this.is_LABEL_START()) { + this.unput(2); + break; + } + this.unput(1); + } else if (ch == '{') { + ch = this.input(); + if (ch == '$') { + this.unput(2); + break; + } + this.unput(1); + } + } + if (ch == '"') { + return this.tok.T_CONSTANT_ENCAPSED_STRING; + } else { + var prefix = 1; + if (this.yytext[0] === 'b' || this.yytext[0] === 'B') { + prefix = 2; + } + if (this.yytext.length > 2) { + this.appendToken( + this.tok.T_ENCAPSED_AND_WHITESPACE, + this.yytext.length - prefix + ); + } + this.unput(this.yytext.length - prefix); + this.begin("ST_DOUBLE_QUOTES"); + return this.yytext; + } + }, + + // check if its a DOC end sequence + isDOC_MATCH: function() { + // @fixme : check if out of text limits + if (this._input.substring(this.offset - 1, this.offset - 1 + this.heredoc_label.length) === this.heredoc_label) { + var ch = this._input[this.offset - 1 + this.heredoc_label.length]; + if (ch === '\n' || ch === '\r' || ch === ';') { + return true; + } + } + return false; + }, + + matchST_NOWDOC: function() { + /** edge case : empty now doc **/ + if (this.isDOC_MATCH()) { + // @fixme : never reached (may be caused by quotes) + this.consume(this.heredoc_label.length); + this.popState(); + return this.tok.T_END_HEREDOC; + } + /** SCANNING CONTENTS **/ + var ch = this._input[this.offset - 1]; + while(this.offset < this.size) { + if (ch === '\n' || ch === '\r') { + ch = this.input(); + if (this.isDOC_MATCH()) { + this.unput(1).popState(); + this.appendToken( + this.tok.T_END_HEREDOC, this.heredoc_label.length + ); + return this.tok.T_ENCAPSED_AND_WHITESPACE; + } + } else { + ch = this.input(); + } + } + // too bad ! reached end of document (will get a parse error) + return this.tok.T_ENCAPSED_AND_WHITESPACE; + }, + + matchST_HEREDOC: function() { + /** edge case : empty here doc **/ + var ch = this.input(); + if (this.isDOC_MATCH()) { + this.consume(this.heredoc_label.length - 1); + this.popState(); + return this.tok.T_END_HEREDOC; + } + /** SCANNING CONTENTS **/ + while(this.offset < this.size) { + + if (ch === '\\') { + ch = this.input(); // ignore next + if (ch !== '\n' && ch !== '\r') { + ch = this.input(); + } + } + + if (ch === '\n' || ch === '\r') { + ch = this.input(); + if (this.isDOC_MATCH()) { + this.unput(1).popState(); + this.appendToken( + this.tok.T_END_HEREDOC, this.heredoc_label.length + ); + return this.tok.T_ENCAPSED_AND_WHITESPACE; + } + } else if (ch === '$') { + ch = this.input(); + if (ch === '{') { + // start of ${ + this.begin('ST_LOOKING_FOR_VARNAME'); + if (this.yytext.length > 2) { + this.appendToken(this.tok.T_DOLLAR_OPEN_CURLY_BRACES, 2); + this.unput(2); + return this.tok.T_ENCAPSED_AND_WHITESPACE; + }else { + return this.tok.T_DOLLAR_OPEN_CURLY_BRACES; + } + } else if (this.is_LABEL_START()) { + // start of $var... + var yyoffset = this.offset; + var next = this.consume_VARIABLE(); + if (this.yytext.length > this.offset - yyoffset + 2) { + this.appendToken(next, this.offset - yyoffset + 2); + this.unput(this.offset - yyoffset + 2); + return this.tok.T_ENCAPSED_AND_WHITESPACE; + } else { + return next; + } + //console.log(this.yytext); + } + } else if (ch === '{') { + ch = this.input(); + if (ch === '$') { + // start of {$... + this.begin('ST_IN_SCRIPTING'); + if (this.yytext.length > 2) { + this.appendToken(this.tok.T_CURLY_OPEN, 1); + this.unput(2); + return this.tok.T_ENCAPSED_AND_WHITESPACE; + } else { + this.unput(1); + return this.tok.T_CURLY_OPEN; + } + } + } else { + ch = this.input(); + } + } + + // too bad ! reached end of document (will get a parse error) + return this.tok.T_ENCAPSED_AND_WHITESPACE; + }, + + consume_VARIABLE: function() { + this.consume_LABEL(); + ch = this.input(); + if (ch == '[') { + this.unput(1); + this.begin('ST_VAR_OFFSET'); + return this.tok.T_VARIABLE; + } else if (ch === '-') { + if (this.input() === '>') { + this.input(); + if (this.is_LABEL_START()) { + this.begin('ST_LOOKING_FOR_PROPERTY'); + } + this.unput(3); + return this.tok.T_VARIABLE; + } else { + this.unput(2); + } + } else { + this.unput(1); + } + return this.tok.T_VARIABLE; + }, + // HANDLES BACKQUOTES + matchST_BACKQUOTE: function() { + + var ch = this.input(); + if (ch === '$') { + ch = this.input(); + if (ch === '{') { + this.begin('ST_LOOKING_FOR_VARNAME'); + return this.tok.T_DOLLAR_OPEN_CURLY_BRACES; + } else if (this.is_LABEL_START()) { + var tok = this.consume_VARIABLE(); + return tok; + } + } else if (ch === '{') { + if (this._input[this.offset] === '$') { + this.begin('ST_IN_SCRIPTING'); + return this.tok.T_CURLY_OPEN; + } + } else if (ch === '`') { + this.popState(); + return '`'; + } + + // any char + while(this.offset < this.size) { + if (ch === '\\') { + this.input(); + } else if (ch === '`') { + this.unput(1); + this.popState(); + this.appendToken('`', 1); + break; + } else if (ch === '$') { + ch = this.input(); + if (ch === '{') { + this.begin('ST_LOOKING_FOR_VARNAME'); + if (this.yytext.length > 2) { + this.appendToken(this.tok.T_DOLLAR_OPEN_CURLY_BRACES, 2); + this.unput(2); + return this.tok.T_ENCAPSED_AND_WHITESPACE; + }else { + return this.tok.T_DOLLAR_OPEN_CURLY_BRACES; + } + } else if (this.is_LABEL_START()) { + // start of $var... + var yyoffset = this.offset; + var next = this.consume_VARIABLE(); + if (this.yytext.length > this.offset - yyoffset + 2) { + this.appendToken(next, this.offset - yyoffset + 2); + this.unput(this.offset - yyoffset + 2); + return this.tok.T_ENCAPSED_AND_WHITESPACE; + } else { + return next; + } + } + this.unput(1); + } else if (ch === '{') { + ch = this.input(); + if (ch === '$') { + // start of {$... + this.begin('ST_IN_SCRIPTING'); + if (this.yytext.length > 2) { + this.appendToken(this.tok.T_CURLY_OPEN, 1); + this.unput(2); + return this.tok.T_ENCAPSED_AND_WHITESPACE; + } else { + this.unput(1); + return this.tok.T_CURLY_OPEN; + } + } + this.unput(1); + } + ch = this.input(); + } + return this.tok.T_ENCAPSED_AND_WHITESPACE; + + }, + + matchST_DOUBLE_QUOTES: function() { + + var ch = this.input(); + if (ch === '$') { + ch = this.input(); + if (ch === '{') { + this.begin('ST_LOOKING_FOR_VARNAME'); + return this.tok.T_DOLLAR_OPEN_CURLY_BRACES; + } else if (this.is_LABEL_START()) { + var tok = this.consume_VARIABLE(); + return tok; + } + } else if (ch === '{') { + if (this._input[this.offset] === '$') { + this.begin('ST_IN_SCRIPTING'); + return this.tok.T_CURLY_OPEN; + } + } else if (ch === '"') { + this.popState(); + return '"'; + } + + // any char + while(this.offset < this.size) { + if (ch === '\\') { + this.input(); + } else if (ch === '"') { + this.unput(1); + this.popState(); + this.appendToken('"', 1); + break; + } else if (ch === '$') { + ch = this.input(); + if (ch === '{') { + this.begin('ST_LOOKING_FOR_VARNAME'); + if (this.yytext.length > 2) { + this.appendToken(this.tok.T_DOLLAR_OPEN_CURLY_BRACES, 2); + this.unput(2); + return this.tok.T_ENCAPSED_AND_WHITESPACE; + }else { + return this.tok.T_DOLLAR_OPEN_CURLY_BRACES; + } + } else if (this.is_LABEL_START()) { + // start of $var... + var yyoffset = this.offset; + var next = this.consume_VARIABLE(); + if (this.yytext.length > this.offset - yyoffset + 2) { + this.appendToken(next, this.offset - yyoffset + 2); + this.unput(this.offset - yyoffset + 2); + return this.tok.T_ENCAPSED_AND_WHITESPACE; + } else { + return next; + } + } + this.unput(1); + } else if (ch === '{') { + ch = this.input(); + if (ch === '$') { + // start of {$... + this.begin('ST_IN_SCRIPTING'); + if (this.yytext.length > 2) { + this.appendToken(this.tok.T_CURLY_OPEN, 1); + this.unput(2); + return this.tok.T_ENCAPSED_AND_WHITESPACE; + } else { + // @fixme : yytext = '"{$' (this.yytext.length > 3) + this.unput(1); + return this.tok.T_CURLY_OPEN; + } + } + if (ch) this.unput(1); + } + ch = this.input(); + } + return this.tok.T_ENCAPSED_AND_WHITESPACE; + } +}; + +},{}],99:[function(require,module,exports){ +/*! + * Copyright (C) 2017 Glayzzle (BSD3 License) + * @authors https://github.com/glayzzle/php-parser/graphs/contributors + * @url http://glayzzle.com + */ +module.exports = { + T_STRING: function() { + var token = this.yytext.toLowerCase(); + var id = this.keywords[token]; + if (!id) { + if (token === 'yield') { + if (this.tryMatch(' from')) { + this.consume(5); + id = this.tok.T_YIELD_FROM; + } else { + id = this.tok.T_YIELD; + } + } else { + id = this.tok.T_STRING; + if (token === 'b' || token === 'B') { + var ch = this.input(1); + if (ch === '"') { + return this.ST_DOUBLE_QUOTES(); + } else if (ch === '\'') { + return this.T_CONSTANT_ENCAPSED_STRING(); + } else if (ch) { + this.unput(1); + } + } + } + } + return id; + }, + // reads a custom token + consume_TOKEN: function() { + var ch = this._input[this.offset - 1]; + var fn = this.tokenTerminals[ch]; + if (fn) { + return fn.apply(this, []); + } else { + return this.yytext; + } + }, + // list of special char tokens + tokenTerminals: { + '$': function() { + this.offset++; + if (this.is_LABEL_START()) { + this.offset--; + this.consume_LABEL(); + return this.tok.T_VARIABLE; + } else { + this.offset--; + return '$'; + } + }, + '-': function() { + var nchar = this._input[this.offset]; + if (nchar === '>') { + this.begin('ST_LOOKING_FOR_PROPERTY').input(); + return this.tok.T_OBJECT_OPERATOR; + } else if (nchar === '-') { + this.input(); + return this.tok.T_DEC; + } else if (nchar === '=') { + this.input(); + return this.tok.T_MINUS_EQUAL; + } + return '-'; + }, + '\\': function() { + return this.tok.T_NS_SEPARATOR; + }, + '/': function() { + if (this._input[this.offset] === '=') { + this.input(); + return this.tok.T_DIV_EQUAL; + } + return '/'; + }, + ':': function() { + if (this._input[this.offset] === ':') { + this.input(); + return this.tok.T_DOUBLE_COLON; + } else { + return ':'; + } + }, + '(': function() { + var initial = this.offset; + this.input(); + if (this.is_TABSPACE()) { + this.consume_TABSPACE().input(); + } + if (this.is_LABEL_START()) { + var yylen = this.yytext.length; + this.consume_LABEL(); + var castToken = this.yytext.substring(yylen - 1).toLowerCase(); + var castId = this.castKeywords[castToken]; + if (castId) { + this.input(); + if (this.is_TABSPACE()) { + this.consume_TABSPACE().input(); + } + if (this._input[this.offset - 1] === ')') { + return castId; + } + } + } + // revert the check + this.unput(this.offset - initial); + return '('; + }, + '=': function() { + var nchar = this._input[this.offset]; + if (nchar === '>') { + this.input(); + return this.tok.T_DOUBLE_ARROW; + } else if (nchar === '=') { + if (this._input[this.offset + 1] === '=') { + this.consume(2); + return this.tok.T_IS_IDENTICAL; + } else { + this.input(); + return this.tok.T_IS_EQUAL; + } + } + return '='; + }, + '+': function() { + var nchar = this._input[this.offset]; + if (nchar === '+') { + this.input(); + return this.tok.T_INC; + } else if (nchar === '=') { + this.input(); + return this.tok.T_PLUS_EQUAL; + } + return '+'; + }, + '!': function() { + if (this._input[this.offset] === '=') { + if (this._input[this.offset + 1] === '=') { + this.consume(2); + return this.tok.T_IS_NOT_IDENTICAL; + } else { + this.input(); + return this.tok.T_IS_NOT_EQUAL; + } + } + return '!'; + }, + '?': function() { + if (this._input[this.offset] === '?') { + this.input(); + return this.tok.T_COALESCE; + } + return '?'; + }, + '<': function() { + var nchar = this._input[this.offset]; + if (nchar === '<') { + nchar = this._input[this.offset + 1]; + if (nchar === '=') { + this.consume(2); + return this.tok.T_SL_EQUAL; + } else if (nchar === '<') { + if (this.is_HEREDOC()) { + return this.tok.T_START_HEREDOC; + } + } + this.input(); + return this.tok.T_SL; + } else if (nchar === '=') { + this.input(); + if (this._input[this.offset] === '>') { + this.input(); + return this.tok.T_SPACESHIP; + } else { + return this.tok.T_IS_SMALLER_OR_EQUAL; + } + } else if (nchar === '>') { + this.input(); + return this.tok.T_IS_NOT_EQUAL; + } + return '<'; + }, + '>': function() { + var nchar = this._input[this.offset]; + if (nchar === '=') { + this.input(); + return this.tok.T_IS_GREATER_OR_EQUAL; + } else if (nchar === '>') { + nchar = this._input[this.offset + 1]; + if (nchar === '=') { + this.consume(2); + return this.tok.T_SR_EQUAL; + } else { + this.input(); + return this.tok.T_SR; + } + } + return '>'; + }, + '*': function() { + var nchar = this._input[this.offset]; + if (nchar === '=') { + this.input(); + return this.tok.T_MUL_EQUAL; + } else if(nchar === '*') { + this.input(); + if (this._input[this.offset] === '=') { + this.input(); + return this.tok.T_POW_EQUAL; + } else { + return this.tok.T_POW; + } + } + return '*'; + }, + '.': function() { + var nchar = this._input[this.offset]; + if (nchar === '=') { + this.input(); + return this.tok.T_CONCAT_EQUAL; + } else if (nchar === '.' && this._input[this.offset + 1] === '.') { + this.consume(2); + return this.tok.T_ELLIPSIS; + } + return '.'; + }, + '%': function() { + if (this._input[this.offset] === '=') { + this.input(); + return this.tok.T_MOD_EQUAL; + } + return '%'; + }, + '&': function() { + var nchar = this._input[this.offset]; + if (nchar === '=') { + this.input(); + return this.tok.T_AND_EQUAL; + } else if (nchar === '&') { + this.input(); + return this.tok.T_BOOLEAN_AND; + } + return '&'; + }, + '|': function() { + var nchar = this._input[this.offset]; + if (nchar === '=') { + this.input(); + return this.tok.T_OR_EQUAL; + } else if (nchar === '|') { + this.input(); + return this.tok.T_BOOLEAN_OR; + } + return '|'; + }, + '^': function() { + if (this._input[this.offset] === '=') { + this.input(); + return this.tok.T_XOR_EQUAL; + } + return '^'; + } + } +}; + +},{}],100:[function(require,module,exports){ +/*! + * Copyright (C) 2017 Glayzzle (BSD3 License) + * @authors https://github.com/glayzzle/php-parser/graphs/contributors + * @url http://glayzzle.com + */ + var tokens = ';:,.\\[]()|^&+-/*=%!~$<>?@'; + +module.exports = { + + // check if the char can be a numeric + is_NUM: function() { + var ch = this._input.charCodeAt(this.offset - 1); + return ch > 47 && ch < 58; + }, + + // check if current char can be a label + is_LABEL: function() { + var ch = this._input.charCodeAt(this.offset - 1); + return (ch > 96 && ch < 123) + || (ch > 64 && ch < 91) + || ch === 95 + || (ch > 47 && ch < 58) + || ch > 126 + ; + }, + + // check if current char can be a label + is_LABEL_START: function() { + var ch = this._input.charCodeAt(this.offset - 1); + return (ch > 96 && ch < 123) + || (ch > 64 && ch < 91) + || ch === 95 + || (ch > 126) + ; + }, + + + // reads each char of the label + consume_LABEL: function() { + while(this.offset < this.size) { + this.input(); + if (!this.is_LABEL()) { + this.unput(1); + break; + } + } + return this; + }, + + // check if current char is a token char + is_TOKEN: function() { + var ch = this._input[this.offset - 1]; + return tokens.indexOf(ch) !== -1; + }, + // check if current char is a whitespace + is_WHITESPACE: function() { + var ch = this._input[this.offset - 1]; + return ch === ' ' || ch === '\t' || ch === '\n' || ch === '\r'; + }, + // check if current char is a whitespace (without newlines) + is_TABSPACE: function() { + var ch = this._input[this.offset - 1]; + return ch === ' ' || ch === '\t'; + }, + // consume all whitespaces (excluding newlines) + consume_TABSPACE: function() { + while(this.offset < this.size) { + this.input(); + if (!this.is_TABSPACE()) { + this.unput(1); + break; + } + } + return this; + }, + // check if current char can be a hexadecimal number + is_HEX: function() { + var ch = this._input.charCodeAt(this.offset - 1); + return (ch > 47 && ch < 58) || (ch > 64 && ch < 71) || (ch > 96 && ch < 103); + } +}; + +},{}],101:[function(require,module,exports){ +/*! + * Copyright (C) 2017 Glayzzle (BSD3 License) + * @authors https://github.com/glayzzle/php-parser/graphs/contributors + * @url http://glayzzle.com + */ + +/** + * @private check if argument is a number + */ +function isNumber(n) { + return n != '.' && n != ',' && !isNaN(parseFloat(n)) && isFinite(n); +} + + +/** + * The PHP Parser class that build the AST tree from the lexer + * @constructor {Parser} + * @property {Lexer} lexer - current lexer instance + * @property {AST} ast - the AST factory instance + * @property {Integer|String} token - current token + * @property {Boolean} extractDoc - should extract documentation as AST node + * @property {Boolean} suppressErrors - should ignore parsing errors and continue + * @property {Boolean} debug - should output debug informations + */ +var parser = function(lexer, ast) { + this.lexer = lexer; + this.ast = ast; + this.tok = lexer.tok; + this.EOF = lexer.EOF; + this.token = null; + this.prev = null; + this.debug = false; + this.extractDoc = false; + this.suppressErrors = false; + this.entries = { + 'VARIABLE': [ + this.tok.T_VARIABLE, + '$', '&', + this.tok.T_NS_SEPARATOR, + this.tok.T_STRING, + this.tok.T_NAMESPACE, + this.tok.T_STATIC + ], + 'SCALAR': [ + this.tok.T_CONSTANT_ENCAPSED_STRING, + this.tok.T_START_HEREDOC, + this.tok.T_LNUMBER, + this.tok.T_DNUMBER, + this.tok.T_ARRAY,'[', + this.tok.T_CLASS_C, + this.tok.T_TRAIT_C, + this.tok.T_FUNC_C, + this.tok.T_METHOD_C, + this.tok.T_LINE, + this.tok.T_FILE, + this.tok.T_DIR, + this.tok.T_NS_C, + '"', + 'b"', + 'B"', + '-', + this.tok.T_NS_SEPARATOR + ], + 'T_MAGIC_CONST': [ + this.tok.T_CLASS_C, + this.tok.T_TRAIT_C, + this.tok.T_FUNC_C, + this.tok.T_METHOD_C, + this.tok.T_LINE, + this.tok.T_FILE, + this.tok.T_DIR, + this.tok.T_NS_C + ], + 'T_MEMBER_FLAGS': [ + this.tok.T_PUBLIC, + this.tok.T_PRIVATE, + this.tok.T_PROTECTED, + this.tok.T_STATIC, + this.tok.T_ABSTRACT, + this.tok.T_FINAL + ], + 'EOS': [ + ';', + this.tok.T_CLOSE_TAG, + this.EOF, + this.tok.T_INLINE_HTML + ], + 'EXPR': [ + '@','-','+','!','~','(','`', + this.tok.T_LIST, + this.tok.T_CLONE, + this.tok.T_INC, + this.tok.T_DEC, + this.tok.T_NEW, + this.tok.T_ISSET, + this.tok.T_EMPTY, + this.tok.T_INCLUDE, + this.tok.T_INCLUDE_ONCE, + this.tok.T_REQUIRE, + this.tok.T_REQUIRE_ONCE, + this.tok.T_EVAL, + this.tok.T_INT_CAST, + this.tok.T_DOUBLE_CAST, + this.tok.T_STRING_CAST, + this.tok.T_ARRAY_CAST, + this.tok.T_OBJECT_CAST, + this.tok.T_BOOL_CAST, + this.tok.T_UNSET_CAST, + this.tok.T_EXIT, + this.tok.T_PRINT, + this.tok.T_YIELD, + this.tok.T_STATIC, + this.tok.T_FUNCTION, + // using VARIABLES : + this.tok.T_VARIABLE, + '$', + this.tok.T_NS_SEPARATOR, + this.tok.T_STRING, + // using SCALAR : + this.tok.T_STRING, // @see variable.js line 45 > conflict with variable = shift/reduce :) + this.tok.T_CONSTANT_ENCAPSED_STRING, + this.tok.T_START_HEREDOC, + this.tok.T_LNUMBER, + this.tok.T_DNUMBER, + this.tok.T_ARRAY,'[', + this.tok.T_CLASS_C, + this.tok.T_TRAIT_C, + this.tok.T_FUNC_C, + this.tok.T_METHOD_C, + this.tok.T_LINE, + this.tok.T_FILE, + this.tok.T_DIR, + this.tok.T_NS_C + ] + }; +}; + +/** + * helper : gets a token name + */ +parser.prototype.getTokenName = function(token) { + if (!isNumber(token)) { + return "'" + token + "'"; + } else { + if (token == this.EOF) return 'the end of file (EOF)'; + return this.lexer.engine.tokens.values[token]; + } +}; + +/** + * main entry point : converts a source code to AST + */ +parser.prototype.parse = function(code, filename) { + this._errors = []; + this.filename = filename || 'eval'; + this.currentNamespace = ['']; + this.lexer.setInput(code); + this.lexer.comment_tokens = this.extractDoc; + this.length = this.lexer._input.length; + this.innerList = false; + var program = this.ast.prepare('program', this); + var childs = []; + this.nextWithComments(); + while(this.token != this.EOF) { + var node = this.read_start(); + if (node !== null && node !== undefined) { + if (Array.isArray(node)) { + childs = childs.concat(node); + } else { + childs.push(node); + } + } + } + return program(childs, this._errors); +}; + +/** + * Raise an error + */ +parser.prototype.raiseError = function(message, msgExpect, expect, token) { + message += ' on line ' + this.lexer.yylloc.first_line; + if (!this.suppressErrors) { + var err = new SyntaxError( + message, this.filename, this.lexer.yylloc.first_line + ); + err.lineNumber = this.lexer.yylloc.first_line; + err.fileName = this.filename; + err.columnNumber = this.lexer.yylloc.first_column + throw err; + } + // Error node : + var node = this.ast.prepare('error', this)( + message, token, this.lexer.yylloc.first_line, expect + ); + this._errors.push(node); + return node; +}; + +/** + * handling errors + */ +parser.prototype.error = function(expect) { + var msg = 'Parse Error : syntax error'; + token = this.getTokenName(this.token); + if (this.token !== this.EOF) { + if (isNumber(this.token)) { + var symbol = this.text(); + if (symbol.length > 10) { + symbol = symbol.substring(0, 7) + '...'; + } + token = '\''+symbol+'\' ('+token+')'; + } + msg += ', unexpected ' + token; + } + var msgExpect = ''; + if (expect && !Array.isArray(expect)) { + if (isNumber(expect) || expect.length === 1) { + msgExpect = ', expecting ' + this.getTokenName(expect); + } + msg += msgExpect; + } + this.token !== this.EOF + return this.raiseError( + msg, + msgExpect, + expect, + token + ); +}; + +/** + * Creates a new AST node + */ +parser.prototype.node = function(name) { + return this.ast.prepare(name, this); +}; + +/** + * expects an end of statement or end of file + * @return {boolean} + */ +parser.prototype.expectEndOfStatement = function() { + if (this.token === ';') { + this.nextWithComments(); + if (this.token === this.tok.T_CLOSE_TAG) { + // strip close tag (statement already closed with ';') + this.nextWithComments(); + } + } else if (this.token === this.tok.T_CLOSE_TAG) { + this.nextWithComments(); + } else if (this.token !== this.tok.T_INLINE_HTML && this.token !== this.EOF) { + this.error(';'); + return false; + } + return true; +}; + +/** outputs some debug information on current token **/ +var ignoreStack = ['parser.next', 'parser.ignoreComments', 'parser.nextWithComments']; +parser.prototype.showlog = function() { + var stack = (new Error()).stack.split('\n'); + var line; + for (var offset = 2; offset < stack.length; offset ++) { + line = stack[offset].trim(); + var found = false; + for(var i = 0; i < ignoreStack.length; i++) { + if (line.substring(3, 3 + ignoreStack[i].length) === ignoreStack[i]) { + found = true; + break; + } + } + if (!found) { + break; + } + } + + console.log( + 'Line ' + + this.lexer.yylloc.first_line + + ' : ' + + this.getTokenName(this.token) + + ">" + this.lexer.yytext + "<" + + ' @-->' + line + ); + return this; +}; + +/** + * Force the parser to check the current token. + * + * If the current token does not match to expected token, + * the an error will be raised. + * + * If the suppressError mode is activated, then the error will + * be added to the program error stack and this function will return `false`. + * + * @param {String|Number} token + * @return {boolean} + * @throws Error + */ +parser.prototype.expect = function(token) { + if (Array.isArray(token)) { + if (token.indexOf(this.token) === -1) { + this.error(token); + return false; + } + } else if (this.token != token) { + this.error(token); + return false; + } + return true; +}; + +/** + * Returns the current token contents + * @return {String} + */ +parser.prototype.text = function() { + return this.lexer.yytext; +}; + +/** consume the next token **/ +parser.prototype.next = function() { + if (this.debug) { + this.showlog(); + this.debug = false; + this.nextWithComments().ignoreComments(); + this.debug = true; + } else { + this.nextWithComments().ignoreComments(); + } + return this; +}; + +/** consume comments (if found) **/ +parser.prototype.ignoreComments = function() { + if (this.debug) this.showlog(); + while(this.token === this.tok.T_COMMENT || this.token === this.tok.T_DOC_COMMENT) { + // IGNORE COMMENTS + this.nextWithComments(); + } + return this; +}; + +/** consume the next token (including doc) **/ +parser.prototype.nextWithComments = function() { + this.prev = [ + this.lexer.yylloc.first_line, + this.lexer.yylloc.first_column, + this.lexer.offset + ]; + this.token = this.lexer.lex() || this.EOF; + if (this.debug) this.showlog(); + return this; +}; + +/** + * Check if token is of specified type + */ +parser.prototype.is = function(type) { + if (Array.isArray(type)) { + return type.indexOf(this.token) !== -1; + } else { + return this.entries[type].indexOf(this.token) != -1; + } +}; + +// extends the parser with syntax files +[ + require('./parser/array.js'), + require('./parser/class.js'), + require('./parser/comment.js'), + require('./parser/expr.js'), + require('./parser/function.js'), + require('./parser/if.js'), + require('./parser/loops.js'), + require('./parser/main.js'), + require('./parser/namespace.js'), + require('./parser/scalar.js'), + require('./parser/statement.js'), + require('./parser/switch.js'), + require('./parser/try.js'), + require('./parser/utils.js'), + require('./parser/variable.js') +].forEach(function (ext) { + for(var k in ext) { + parser.prototype[k] = ext[k]; + } +}); + +module.exports = parser; + +},{"./parser/array.js":102,"./parser/class.js":103,"./parser/comment.js":104,"./parser/expr.js":105,"./parser/function.js":106,"./parser/if.js":107,"./parser/loops.js":108,"./parser/main.js":109,"./parser/namespace.js":110,"./parser/scalar.js":111,"./parser/statement.js":112,"./parser/switch.js":113,"./parser/try.js":114,"./parser/utils.js":115,"./parser/variable.js":116}],102:[function(require,module,exports){ +/*! + * Copyright (C) 2017 Glayzzle (BSD3 License) + * @authors https://github.com/glayzzle/php-parser/graphs/contributors + * @url http://glayzzle.com + */ +var ArrayExpr = 'array'; +var ArrayEntry = 'entry'; + +module.exports = { + /** + * Parse an array + * ```ebnf + * array ::= T_ARRAY '(' array_pair_list ')' | + * '[' array_pair_list ']' + * ``` + */ + read_array: function() { + var expect = null; + var shortForm = false; + var items = []; + var result = this.node(ArrayExpr); + + if (this.token === this.tok.T_ARRAY) { + this.next().expect('('); + expect = ')'; + } else { + shortForm = true; + expect = ']'; + } + + if (this.next().token != expect) { + while(this.token != this.EOF) { + items.push(this.read_array_pair_list()); + if (this.token == ',') { + this.next(); + if (this.token === expect) { + break; + } + } else break; + } + } + this.expect(expect); + this.next(); + return result(shortForm, items); + }, + /** + * Reads an array entry item + * ```ebnf + * array_pair_list ::= '&' w_variable | + * ( + * expr ( + * T_DOUBLE_ARROW ( + * expr | '&' w_variable + * ) + * )? + * ) + * ``` + */ + read_array_pair_list: function() { + var result = this.node(ArrayEntry); + var key = null; + var value = null; + if (this.token === '&') { + value = this.next().read_variable(true, false, true); + } else { + var expr = this.read_expr(); + if (this.token === this.tok.T_DOUBLE_ARROW) { + key = expr; + if (this.next().token === '&') { + value = this.next().read_variable(true, false, true); + } else { + value = this.read_expr(); + } + } else { + value = expr; + } + } + return result(key, value); + }, + /** + * ```ebnf + * dim_offset ::= expr? + * ``` + */ + read_dim_offset: function() { + if (this.token == ']') return false; + return this.read_expr(); + } +}; + +},{}],103:[function(require,module,exports){ +/*! + * Copyright (C) 2017 Glayzzle (BSD3 License) + * @authors https://github.com/glayzzle/php-parser/graphs/contributors + * @url http://glayzzle.com + */ + +module.exports = { + /** + * reading a class + * ```ebnf + * class ::= class_scope? T_CLASS T_STRING (T_EXTENDS NAMESPACE_NAME)? (T_IMPLEMENTS (NAMESPACE_NAME ',')* NAMESPACE_NAME)? '{' CLASS_BODY '}' + * ``` + */ + read_class: function(flag) { + var result = this.node('class'); + this.expect(this.tok.T_CLASS); + this.next().expect(this.tok.T_STRING); + var propName = this.text() + , propExtends = null + , propImplements = null + , body + ; + if (this.next().token == this.tok.T_EXTENDS) { + propExtends = this.next().read_namespace_name(); + } + if (this.token == this.tok.T_IMPLEMENTS) { + propImplements = this.next().read_name_list(); + } + this.expect('{'); + body = this.nextWithComments().read_class_body(); + return result( + propName + ,propExtends + ,propImplements + ,body + ,flag + ); + } + /** + * Read the class visibility + * ```ebnf + * class_scope ::= (T_FINAL | T_ABSTRACT)? + * ``` + */ + ,read_class_scope: function() { + var result = this.token; + if (result == this.tok.T_FINAL) { + this.next(); + return [0, 0, 2]; + } else if (result == this.tok.T_ABSTRACT) { + this.next(); + return [0, 0, 1]; + } + return [0, 0, 0]; + } + /** + * Reads a class body + * ```ebnf + * class_body ::= (member_flags? (T_VAR | T_STRING | T_FUNCTION))* + * ``` + */ + ,read_class_body: function() { + var result = []; + + while(this.token !== this.EOF && this.token !== '}') { + + if (this.token === this.tok.T_COMMENT) { + result.push(this.read_comment()); + continue; + } + + if (this.token === this.tok.T_DOC_COMMENT) { + result.push(this.read_doc_comment()); + continue; + } + + // check T_USE trait + if (this.token === this.tok.T_USE) { + result = result.concat( + this.next().read_trait_use_statement() + ); + continue; + } + + // read member flags + var flags = this.read_member_flags(false); + + // check constant + if (this.token === this.tok.T_CONST) { + var constants = this.read_constant_list(flags); + this.expect(';'); + this.nextWithComments(); + result = result.concat(constants); + continue; + } + + // jump over T_VAR then land on T_VARIABLE + if (this.token === this.tok.T_VAR) { + this.next().expect(this.tok.T_VARIABLE); + flags[0] = flags[1] = 0; // public & non static var + } + + if (this.token === this.tok.T_VARIABLE) { + + // reads a variable + var variables = this.read_variable_list(flags); + this.expect(';'); + this.nextWithComments(); + result = result.concat(variables); + + } else if (this.token === this.tok.T_FUNCTION) { + + // reads a function + result.push(this.read_function(false, flags)); + + } else { + + // raise an error + this.error([ + this.tok.T_CONST, + this.tok.T_VARIABLE, + this.tok.T_FUNCTION + ]); + // ignore token + this.next(); + + } + } + this.expect('}'); + this.nextWithComments(); + return result; + } + /** + * Reads variable list + * ```ebnf + * variable_list ::= (variable_declaration ',')* variable_declaration + * ``` + */ + ,read_variable_list: function(flags) { + return this.read_list( + /** + * Reads a variable declaration + * + * ```ebnf + * variable_declaration ::= T_VARIABLE '=' scalar + * ``` + */ + function read_variable_declaration() { + var result = this.node('property'); + this.expect(this.tok.T_VARIABLE); + var name = this.text().substring(1); // ignore $ + this.next(); + if (this.token === ';' || this.token === ',') { + return result(name, null, flags); + } else if(this.token === '=') { + // https://github.com/php/php-src/blob/master/Zend/zend_language_parser.y#L815 + return result(name, this.next().read_expr(), flags); + } else { + this.expect([',', ';', '=']); + return result(name, null, flags); + } + }, ',' + ); + } + /** + * Reads constant list + * ```ebnf + * constant_list ::= T_CONST (constant_declaration ',')* constant_declaration + * ``` + */ + ,read_constant_list: function(flags) { + if (this.expect(this.tok.T_CONST)) { + this.next(); + } + return this.read_list( + /** + * Reads a constant declaration + * + * ```ebnf + * constant_declaration ::= T_STRING '=' expr + * ``` + * @return {Constant} [:link:](AST.md#constant) + */ + function read_constant_declaration() { + var result = this.node('classconstant'), name = null, value = null; + if (this.expect(this.tok.T_STRING)) { + name = this.text(); + this.next(); + } + if (this.expect('=')) { + value = this.next().read_expr(); + } + return result(name, value, flags); + }, ',' + ) + ; + } + /** + * Read member flags + * @return array + * 1st index : 0 => public, 1 => protected, 2 => private + * 2nd index : 0 => instance member, 1 => static member + * 3rd index : 0 => normal, 1 => abstract member, 2 => final member + */ + ,read_member_flags: function(asInterface) { + var result = [-1, -1, -1]; + if (this.is('T_MEMBER_FLAGS')) { + var idx = 0, val = 0; + do { + switch(this.token) { + case this.tok.T_PUBLIC: idx = 0; val = 0; break; + case this.tok.T_PROTECTED: idx = 0; val = 1; break; + case this.tok.T_PRIVATE: idx = 0; val = 2; break; + case this.tok.T_STATIC: idx = 1; val = 1; break; + case this.tok.T_ABSTRACT: idx = 2; val = 1; break; + case this.tok.T_FINAL: idx = 2; val = 2; break; + } + if (asInterface) { + if (idx == 0 && val == 2) { + // an interface can't be private + this.expect([this.tok.T_PUBLIC, this.tok.T_PROTECTED]); + val = -1; + } else if (idx == 2 && val == 1) { + // an interface cant be abstract + this.error(); + val = -1; + } + } + if (result[idx] !== -1) { + // already defined flag + this.error(); + } else if (val !== -1) { + result[idx] = val; + } + } while(this.next().is('T_MEMBER_FLAGS')); + } + + if (result[0] == -1) result[0] = 0; + if (result[1] == -1) result[1] = 0; + if (result[2] == -1) result[2] = 0; + return result; + } + /** + * reading an interface + * ```ebnf + * interface ::= T_INTERFACE T_STRING (T_EXTENDS (NAMESPACE_NAME ',')* NAMESPACE_NAME)? '{' INTERFACE_BODY '}' + * ``` + */ + ,read_interface: function() { + var result = this.node('interface'), name = null, body = null, propExtends = null; + if (this.expect(this.tok.T_INTERFACE)) { + this.next(); + } + if (this.expect(this.tok.T_STRING)) { + name = this.text(); + this.next(); + } + if (this.token === this.tok.T_EXTENDS) { + propExtends = this.next().read_name_list(); + } + if (this.expect('{')) { + body = this.next().read_interface_body(); + } + return result(name, propExtends, body); + } + /** + * Reads an interface body + * ```ebnf + * interface_body ::= (member_flags? (T_CONST | T_FUNCTION))* + * ``` + */ + ,read_interface_body: function() { + var result = []; + + while(this.token !== this.EOF && this.token !== '}') { + + if (this.token === this.tok.T_COMMENT) { + result.push(this.read_comment()); + continue; + } + + if (this.token === this.tok.T_DOC_COMMENT) { + result.push(this.read_doc_comment()); + continue; + } + + // read member flags + var flags = this.read_member_flags(true); + + // check constant + if (this.token == this.tok.T_CONST) { + var constants = this.read_constant_list(flags); + if (this.expect(';')) { + this.nextWithComments(); + } + result = result.concat(constants); + } + + // reads a function + else if (this.token === this.tok.T_FUNCTION) { + var method = this.read_function_declaration(2, flags); + method.parseFlags(flags); + result.push(method); + if (this.expect(';')) { + this.nextWithComments(); + } + } else { + // raise an error + this.error([ + this.tok.T_CONST, + this.tok.T_FUNCTION + ]); + this.next(); + } + } + if (this.expect('}')) { + this.next(); + } + return result; + } + /** + * reading a trait + * ```ebnf + * trait ::= T_TRAIT T_STRING (T_EXTENDS (NAMESPACE_NAME ',')* NAMESPACE_NAME)? '{' FUNCTION* '}' + * ``` + */ + ,read_trait: function(flag) { + var result = this.node('trait'), + propName = null, + propExtends = null, + propImplements = null, + body = null; + if (this.expect(this.tok.T_TRAIT)) { + this.next(); + } + if (this.expect(this.tok.T_STRING)) { + propName = this.text(); + } + if (this.next().token == this.tok.T_EXTENDS) { + propExtends = this.next().read_namespace_name(); + } + if (this.token == this.tok.T_IMPLEMENTS) { + propImplements = this.next().read_name_list(); + } + if (this.expect('{')) { + body = this.next().read_class_body(); + } + return result( + propName, + propExtends, + propImplements, + body + ); + } + /** + * reading a use statement + * ```ebnf + * trait_use_statement ::= namespace_name (',' namespace_name)* ('{' trait_use_alias '}')? + * ``` + */ + ,read_trait_use_statement: function() { + // defines use statements + var node = this.node('traituse'); + var traits = [this.read_namespace_name()]; + var adaptations = null; + while(this.token === ',') { + traits.push( + this.next().read_namespace_name() + ); + } + if (this.token === '{') { + adaptations = []; + // defines alias statements + while(this.next().token !== this.EOF) { + if (this.token === '}') break; + adaptations.push(this.read_trait_use_alias()); + this.expect(';'); + } + if (this.expect('}')) { + this.nextWithComments(); + } + } else { + if (this.expect(';')) { + this.nextWithComments(); + } + } + return node(traits, adaptations); + } + /** + * Reading trait alias + * ```ebnf + * trait_use_alias ::= namespace_name ( T_DOUBLE_COLON T_STRING )? (T_INSTEADOF namespace_name) | (T_AS member_flags? T_STRING) + * ``` + */ + ,read_trait_use_alias: function() { + var node = this.node(); + var trait = null; + var method = this.read_namespace_name(); + + if (this.token === this.tok.T_DOUBLE_COLON) { + if (this.next().expect(this.tok.T_STRING)) { + trait = method; + method = this.text(); + this.next(); + } + } else { + // convert identifier as string + method = method.name; + } + + // handle trait precedence + if (this.token === this.tok.T_INSTEADOF) { + return node( + 'traitprecedence', + trait, method, + this.next().read_name_list() + ); + } + + // handle trait alias + else if (this.token === this.tok.T_AS) { + var flags = false; + var alias = null; + if (this.next().is('T_MEMBER_FLAGS')) { + flags = this.read_member_flags(); + } + + if (this.token === this.tok.T_STRING) { + alias = this.text(); + this.next(); + } else if (flags === false) { + // no visibility flags and no name => too bad + this.expect(this.tok.T_STRING); + } + + return node('traitalias', trait, method, alias, flags) + } + + // handle errors + this.expect([this.tok.T_AS, this.tok.T_INSTEADOF]); + return node('traitalias', trait, method, null, null); + } +}; + +},{}],104:[function(require,module,exports){ +/*! + * Copyright (C) 2017 Glayzzle (BSD3 License) + * @authors https://github.com/glayzzle/php-parser/graphs/contributors + * @url http://glayzzle.com + */ + +var docSplit = /^(\s*\*[ \t]*|[ \t]*)(.*)$/gm; + +module.exports = { + /** + * Comments with // or # or / * ... * / + */ + read_comment: function() { + var result = this.node('doc'); + var lines = []; + do { + var line = this.text(); + if (line[0] === '#') { + line = line.substring(1); + } else { + line = line.substring(2); + if (line.substring(line.length - 2) === '*/') { + line = line.substring(0, line.length - 2); + } + } + lines.push(line.trim()); + } while(this.nextWithComments().token === this.tok.T_COMMENT); + return result(false, lines); + }, + /** + * Comments with / ** ... * / + */ + read_doc_comment: function() { + var result = this.node('doc'); + var text = this.text(); + text = text.substring(2, text.length - 2); + var lines = []; + text = text.split(docSplit); + for(var i = 2; i < text.length; i += 3) { + lines.push(text[i].trim()); + } + this.nextWithComments(); + return result(true, lines); + } +}; + +},{}],105:[function(require,module,exports){ +/*! + * Copyright (C) 2017 Glayzzle (BSD3 License) + * @authors https://github.com/glayzzle/php-parser/graphs/contributors + * @url http://glayzzle.com + */ +"use strict"; + +module.exports = { + + read_expr: function() { + var result = this.node(); + var expr = this.read_expr_item(); + // binary operations + if (this.token === '|') + return result('bin', '|', expr, this.next().read_expr()); + if (this.token === '&') + return result('bin', '&', expr, this.next().read_expr()); + if (this.token === '^') + return result('bin', '^', expr, this.next().read_expr()); + if (this.token === '.') + return result('bin', '.', expr, this.next().read_expr()); + if (this.token === '+') + return result('bin', '+', expr, this.next().read_expr()); + if (this.token === '-') + return result('bin', '-', expr, this.next().read_expr()); + if (this.token === '*') + return result('bin', '*', expr, this.next().read_expr()); + if (this.token === '/') + return result('bin', '/', expr, this.next().read_expr()); + if (this.token === '%') + return result('bin', '%', expr, this.next().read_expr()); + if (this.token === this.tok.T_POW) + return result('bin', '**', expr, this.next().read_expr()); + if (this.token === this.tok.T_SL) + return result('bin', '<<', expr, this.next().read_expr()); + if (this.token === this.tok.T_SR) + return result('bin', '>>', expr, this.next().read_expr()); + // more binary operations (formerly bool) + if (this.token === this.tok.T_BOOLEAN_OR) + return result('bin', '||', expr, this.next().read_expr()); + if (this.token === this.tok.T_LOGICAL_OR) + return result('bin', 'or', expr, this.next().read_expr()); + if (this.token === this.tok.T_BOOLEAN_AND) + return result('bin', '&&', expr, this.next().read_expr()); + if (this.token === this.tok.T_LOGICAL_AND) + return result('bin', 'and', expr, this.next().read_expr()); + if (this.token === this.tok.T_LOGICAL_XOR) + return result('bin', 'xor', expr, this.next().read_expr()); + if (this.token === this.tok.T_IS_IDENTICAL) + return result('bin', '===', expr, this.next().read_expr()); + if (this.token === this.tok.T_IS_NOT_IDENTICAL) + return result('bin', '!==', expr, this.next().read_expr()); + if (this.token === this.tok.T_IS_EQUAL) + return result('bin', '==', expr, this.next().read_expr()); + if (this.token === this.tok.T_IS_NOT_EQUAL) + return result('bin', '!=', expr, this.next().read_expr()); + if (this.token === '<') + return result('bin', '<', expr, this.next().read_expr()); + if (this.token === '>') + return result('bin', '>', expr, this.next().read_expr()); + if (this.token === this.tok.T_IS_SMALLER_OR_EQUAL) + return result('bin', '<=', expr, this.next().read_expr()); + if (this.token === this.tok.T_IS_GREATER_OR_EQUAL) + return result('bin', '>=', expr, this.next().read_expr()); + if (this.token === this.tok.T_SPACESHIP) + return result('bin', '<=>', expr, this.next().read_expr()); + if (this.token === this.tok.T_INSTANCEOF) + return result('bin', 'instanceof', expr, this.next().read_expr()); + + // extra operations : + // $username = $_GET['user'] ?? 'nobody'; + if (this.token === this.tok.T_COALESCE) + return result('bin', '??', expr, this.next().read_expr()); + + // extra operations : + // $username = $_GET['user'] ? true : false; + if (this.token === '?') { + var trueArg = null; + if (this.next().token !== ':') { + trueArg = this.read_expr(); + } + this.expect(':') && this.next(); + return result('retif', expr, trueArg, this.read_expr()); + } + + return expr; + } + + /** + * ```ebnf + * Reads an expression + * expr ::= @todo + * ``` + */ + ,read_expr_item: function() { + + if (this.token === '@') + return this.node('silent')(this.next().read_expr()); + if (this.token === '+') + return this.node('unary')('+', this.next().read_expr()); + if (this.token === '!') + return this.node('unary')('!', this.next().read_expr()); + if (this.token === '~') + return this.node('unary')('~', this.next().read_expr()); + + if (this.token === '-') { + var result = this.node(); + this.next(); + if ( + this.token === this.tok.T_LNUMBER || + this.token === this.tok.T_DNUMBER + ) { + // negative number + result = result('number', '-' + this.text()); + this.next(); + return result; + } else { + return result('unary', '-', this.read_expr()); + } + } + + if (this.token === '(') { + var node = this.node('parenthesis'); + var expr = this.next().read_expr(); + this.expect(')') && this.next(); + expr = node(expr); + // handle dereferencable + if (this.token === this.tok.T_OBJECT_OPERATOR) { + return this.recursive_variable_chain_scan(expr, false); + } else if (this.token === this.tok.T_CURLY_OPEN || this.token === '[') { + return this.read_dereferencable(expr); + } else if (this.token === '(') { + // https://github.com/php/php-src/blob/master/Zend/zend_language_parser.y#L1118 + return this.node('call')( + expr, this.read_function_argument_list() + ); + } else { + return expr; + } + } + + if (this.token === '`') { + // https://github.com/php/php-src/blob/master/Zend/zend_language_parser.y#L1048 + return this.next().read_encapsed_string('`'); + } + + if (this.token === this.tok.T_LIST) { + var result = this.node('list'), assign = null; + var isInner = this.innerList; + if (!isInner) { + assign = this.node('assign'); + } + if (this.next().expect('(')) { + this.next(); + } + + if (!this.innerList) this.innerList = true; + var assignList = this.read_assignment_list(); + + // check if contains at least one assignment statement + var hasItem = false; + for(var i = 0; i < assignList.length; i++) { + if (assignList[i] !== null) { + hasItem = true; + break; + } + } + if (!hasItem) { + this.raiseError( + 'Fatal Error : Cannot use empty list on line ' + this.lexer.yylloc.first_line + ); + } + if (this.expect(')')) { + this.next(); + } + + if (!isInner) { + this.innerList = false; + if (this.expect('=')) { + return assign( + result(assignList), + this.next().read_expr(), + '=' + ); + } else { + // fallback : list($a, $b); + return result(assignList); + } + } else { + return result(assignList); + } + } + + if (this.token === this.tok.T_CLONE) + return this.node('clone')( + this.next().read_expr() + ); + + switch(this.token) { + + case this.tok.T_INC: + return this.node('pre')( + '+', this.next().read_variable(false, false, false) + ); + + case this.tok.T_DEC: + return this.node('pre')( + '-', this.next().read_variable(false, false, false) + ); + + case this.tok.T_NEW: + return this.next().read_new_expr(); + + case this.tok.T_ISSET: + var result = this.node('isset'); + if (this.next().expect('(')) { + this.next(); + } + var args = this.read_list(this.read_expr, ','); + if (this.expect(')')) { + this.next(); + } + return result(args); + + case this.tok.T_EMPTY: + var result = this.node('empty'); + if (this.next().expect('(')) { + this.next(); + } + var arg = this.read_expr(); + if (this.expect(')')) { + this.next(); + } + return result([arg]); + + case this.tok.T_INCLUDE: + return this.node('include')( + false, false, + this.next().read_expr() + ); + + case this.tok.T_INCLUDE_ONCE: + return this.node('include')( + true, false, + this.next().read_expr() + ); + + case this.tok.T_REQUIRE: + return this.node('include')( + false, true, + this.next().read_expr() + ); + + case this.tok.T_REQUIRE_ONCE: + return this.node('include')( + true, true, + this.next().read_expr() + ); + + case this.tok.T_EVAL: + var result = this.node('eval'); + if (this.next().expect('(')) { + this.next(); + } + var expr = this.read_expr(); + if (this.expect(')')) { + this.next(); + } + return result(expr); + + case this.tok.T_INT_CAST: + return this.node('cast')('int', this.next().read_expr()); + + case this.tok.T_DOUBLE_CAST: + return this.node('cast')('double', this.next().read_expr()); + + case this.tok.T_STRING_CAST: + return this.node('cast')('string', this.next().read_expr()); + + case this.tok.T_ARRAY_CAST: + return this.node('cast')('array', this.next().read_expr()); + + case this.tok.T_OBJECT_CAST: + return this.node('cast')('object', this.next().read_expr()); + + case this.tok.T_BOOL_CAST: + return this.node('cast')('boolean', this.next().read_expr()); + + case this.tok.T_UNSET_CAST: + return this.node('unset')( + this.next().read_expr() + ); + + case this.tok.T_EXIT: + var result = this.node('exit'); + var status = null; + if ( this.next().token === '(' ) { + if (this.next().token !== ')') { + status = this.read_expr(); + if (this.expect(')')) { + this.next(); + } + } else { + this.next(); + } + } + return result(status); + + case this.tok.T_PRINT: + return this.node('print')( + this.next().read_expr() + ); + + // T_YIELD (expr (T_DOUBLE_ARROW expr)?)? + case this.tok.T_YIELD: + var result = this.node('yield'), value = null, key = null; + if (this.next().is('EXPR')) { + // reads the yield return value + value = this.read_expr(); + if (this.token === this.tok.T_DOUBLE_ARROW) { + // reads the yield returned key + key = value; + value = this.next().read_expr(); + } + } + return result(value, key); + + // T_YIELD_FROM expr + case this.tok.T_YIELD_FROM: + var result = this.node('yieldfrom'); + var expr = this.next().read_expr(); + return result(expr); + + case this.tok.T_FUNCTION: + // @fixme later - removed static lambda function declarations (colides with static keyword usage) + return this.read_function(true); + + } + + // SCALAR | VARIABLE + var expr; + if (this.is('VARIABLE')) { + var result = this.node(); + expr = this.read_variable(false, false, false); + // VARIABLES SPECIFIC OPERATIONS + switch(this.token) { + case '=': + var right; + if (this.next().token == '&') { + if (this.next().token === this.tok.T_NEW) { + right = this.next().read_new_expr(); + } else { + right = this.read_variable(false, false, true); + } + } else { + right = this.read_expr(); + } + return result('assign', expr, right, '='); + + // operations : + case this.tok.T_PLUS_EQUAL: + return result('assign', expr, this.next().read_expr(), '+='); + + case this.tok.T_MINUS_EQUAL: + return result('assign', expr, this.next().read_expr(), '-='); + + case this.tok.T_MUL_EQUAL: + return result('assign', expr, this.next().read_expr(), '*='); + + case this.tok.T_POW_EQUAL: + return result('assign', expr, this.next().read_expr(), '**='); + + case this.tok.T_DIV_EQUAL: + return result('assign', expr, this.next().read_expr(), '/='); + + case this.tok.T_CONCAT_EQUAL: + return result('assign', expr, this.next().read_expr(), '.='); + + case this.tok.T_MOD_EQUAL: + return result('assign', expr, this.next().read_expr(), '%='); + + case this.tok.T_AND_EQUAL: + return result('assign', expr, this.next().read_expr(), '&='); + + case this.tok.T_OR_EQUAL: + return result('assign', expr, this.next().read_expr(), '|='); + + case this.tok.T_XOR_EQUAL: + return result('assign', expr, this.next().read_expr(), '^='); + + case this.tok.T_SL_EQUAL: + return result('assign', expr, this.next().read_expr(), '<<='); + + case this.tok.T_SR_EQUAL: + return result('assign',expr, this.next().read_expr(), '>>='); + + case this.tok.T_INC: + this.next(); + return result('post', '+', expr); + case this.tok.T_DEC: + this.next(); + return result('post', '-', expr); + } + } else if (this.is('SCALAR')) { + expr = this.read_scalar(); + // handle dereferencable + while(this.token !== this.EOF) { + if (this.token === this.tok.T_OBJECT_OPERATOR) { + expr = this.recursive_variable_chain_scan(expr, false); + } else if (this.token === this.tok.T_CURLY_OPEN || this.token === '[') { + expr = this.read_dereferencable(expr); + } else if (this.token === '(') { + // https://github.com/php/php-src/blob/master/Zend/zend_language_parser.y#L1118 + expr = this.node('call')(expr, this.read_function_argument_list()); + } else { + return expr; + } + } + } else { + this.error('EXPR'); + this.next(); + } + + // returns variable | scalar + return expr; + + } + /** + * ```ebnf + * new_expr ::= T_NEW (namespace_name function_argument_list) | (T_CLASS ... class declaration) + * ``` + * https://github.com/php/php-src/blob/master/Zend/zend_language_parser.y#L850 + */ + ,read_new_expr: function() { + var result = this.node('new'); + if (this.token === this.tok.T_CLASS) { + var what = this.node('class'); + // Annonymous class declaration + var propExtends = null, propImplements = null, body = null, args = []; + if (this.next().token === '(') { + args = this.read_function_argument_list(); + } + if (this.token == this.tok.T_EXTENDS) { + propExtends = this.next().read_namespace_name(); + } + if (this.token == this.tok.T_IMPLEMENTS) { + propImplements = this.next().read_name_list(); + } + if (this.expect('{')) { + body = this.next().read_class_body(); + } + return result( + what( + null + ,propExtends + ,propImplements + ,body + ,[0, 0, 0] + ), args + ); + } else { + // Already existing class + var name = this.read_class_name_reference(); + var args = []; + if (this.token === '(') { + args = this.read_function_argument_list(); + } + return result(name, args); + } + } + /** + * Reads a class name + * ```ebnf + * class_name_reference ::= namespace_name | variable + * ``` + */ + ,read_class_name_reference: function() { + if ( + this.token === this.tok.T_NS_SEPARATOR || + this.token === this.tok.T_STRING || + this.token === this.tok.T_NAMESPACE + ) { + var result = this.read_namespace_name(); + if (this.token === this.tok.T_DOUBLE_COLON) { + result = this.read_static_getter(result); + } + return result; + } else if (this.is('VARIABLE')) { + return this.read_variable(true, false, false); + } else { + this.expect([this.tok.T_STRING, 'VARIABLE']); + } + } + /** + * ```ebnf + * assignment_list ::= assignment_list_element (',' assignment_list_element?)* + * ``` + */ + ,read_assignment_list: function() { + return this.read_list( + this.read_assignment_list_element, ',' + ); + } + + /** + * ```ebnf + * assignment_list_element ::= expr | expr T_DOUBLE_ARROW expr + * ``` + */ + ,read_assignment_list_element: function() { + if (this.token === ',' || this.token === ')') return null; + var result = this.read_expr_item(); + if (this.token === this.tok.T_DOUBLE_ARROW) { + result = [ + 'key', + result, + this.next().read_expr_item() + ]; + } + return result; + } +}; + +},{}],106:[function(require,module,exports){ +/*! + * Copyright (C) 2017 Glayzzle (BSD3 License) + * @authors https://github.com/glayzzle/php-parser/graphs/contributors + * @url http://glayzzle.com + */ + +module.exports = { + /** + * checks if current token is a reference keyword + */ + is_reference: function() { + if (this.token == '&') { + this.next(); + return true; + } + return false; + } + /** + * checks if current token is a variadic keyword + */ + ,is_variadic: function() { + if (this.token === this.tok.T_ELLIPSIS) { + this.next(); + return true; + } + return false; + } + /** + * reading a function + * ```ebnf + * function ::= function_declaration code_block + * ``` + */ + ,read_function: function(closure, flag) { + var result = this.read_function_declaration( + closure ? 1 : (flag ? 2 : 0) + ); + if (flag && flag[2] == 1) { + // abstract function : + result.parseFlags(flag); + if (this.expect(';')) { + this.nextWithComments(); + } + } else { + if (this.expect('{')) { + result.body = this.read_code_block(false); + if (result.loc && result.body.loc) { + result.loc.end = result.body.loc.end; + } + } + if (flag) { + result.parseFlags(flag); + } + } + return result; + } + /** + * reads a function declaration (without his body) + * ```ebnf + * function_declaration ::= T_FUNCTION '&'? T_STRING '(' parameter_list ')' + * ``` + */ + ,read_function_declaration: function(type) { + var nodeName = 'function'; + if (type === 1) { + nodeName = 'closure'; + } else if (type === 2) { + nodeName = 'method'; + } + var result = this.node(nodeName); + if (this.expect(this.tok.T_FUNCTION)) { + this.next(); + } + var isRef = this.is_reference(); + var name = false, use = [], returnType = null, nullable = false; + if (type !== 1) { + if (this.expect(this.tok.T_STRING)) { + name = this.text(); + this.next(); + } + } + if (this.expect('(')) this.next(); + var params = this.read_parameter_list(); + if (this.expect(')')) this.next(); + if (type === 1 && this.token === this.tok.T_USE) { + if (this.next().expect('(')) this.next(); + use = this.read_list(this.read_lexical_var, ','); + if (this.expect(')')) this.next(); + } + if (this.token === ':') { + if (this.next().token === '?') { + nullable = true; + this.next(); + } + returnType = this.read_type(); + } + if (type === 1) { + // closure + return result(params, isRef, use, returnType, nullable); + } + return result(name, params, isRef, returnType, nullable); + } + /** + * ```ebnf + * lexical_var ::= '&'? T_VARIABLE + * ``` + */ + ,read_lexical_var: function() { + var result = this.node('variable'); + var isRef = false; + if (this.token === '&') { + isRef = true; + this.next(); + } + this.expect(this.tok.T_VARIABLE); + var name = this.text().substring(1); + this.next(); + return result(name, isRef); + } + /** + * reads a list of parameters + * ```ebnf + * parameter_list ::= (parameter ',')* parameter? + * ``` + */ + ,read_parameter_list: function() { + var result = []; + if (this.token != ')') { + while(this.token != this.EOF) { + result.push(this.read_parameter()); + if (this.token == ',') { + this.next(); + } else if (this.token == ')') { + break; + } else { + this.error([',', ')']); + break; + } + } + } + return result; + } + /** + * ```ebnf + * parameter ::= type? '&'? T_ELLIPSIS? T_VARIABLE ('=' expr)? + * ``` + * @see https://github.com/php/php-src/blob/493524454d66adde84e00d249d607ecd540de99f/Zend/zend_language_parser.y#L640 + */ + ,read_parameter: function() { + var node = this.node('parameter'), + name = null, + value = null, + type = null, + nullable = false; + if (this.token === '?') { + this.next(); + nullable = true; + } + type = this.read_type(); + if (nullable && !type) { + this.raiseError('Expecting a type definition combined with nullable operator'); + } + var isRef = this.is_reference(); + var isVariadic = this.is_variadic(); + if (this.expect(this.tok.T_VARIABLE)) { + name = this.text().substring(1); + this.next(); + } + if (this.token == '=') { + value = this.next().read_expr(); + } + return node(name, type, value, isRef, isVariadic, nullable); + } + /** + * Reads a list of arguments + * ```ebnf + * function_argument_list ::= '(' (argument_list (',' argument_list)*)? ')' + * ``` + */ + ,read_function_argument_list: function() { + var result = []; + var wasVariadic = false; + this.expect('(') && this.next(); + if (this.token !== ')') { + while(this.token != this.EOF) { + var argument = this.read_argument_list(); + result.push(argument); + if (argument.kind === 'variadic') { + wasVariadic = true; + } else if (wasVariadic) { + this.raiseError('Unexpected argument after a variadic argument'); + } + if (this.token === ',') { + this.next(); + } else break; + } + } + this.expect(')') && this.next(); + return result; + } + /** + * ```ebnf + * argument_list ::= T_ELLIPSIS? expr + * ``` + */ + ,read_argument_list: function() { + if (this.token === this.tok.T_ELLIPSIS ) { + return this.node('variadic')(this.next().read_expr()); + } + return this.read_expr(); + } + /** + * read type hinting + * ```ebnf + * type ::= T_ARRAY | T_CALLABLE | namespace_name + * ``` + */ + ,read_type: function() { + var result = this.node('identifier'); + switch(this.token) { + case this.tok.T_ARRAY: + this.next(); + return result(['', 'array'], false); + case this.tok.T_NAMESPACE: + case this.tok.T_NS_SEPARATOR: + case this.tok.T_STRING: + return this.read_namespace_name(); + case this.tok.T_CALLABLE: + this.next(); + return result(['', 'callable'], false); + default: + return null; + } + } +}; + +},{}],107:[function(require,module,exports){ +/*! + * Copyright (C) 2017 Glayzzle (BSD3 License) + * @authors https://github.com/glayzzle/php-parser/graphs/contributors + * @url http://glayzzle.com + */ + +module.exports = { + /** + * Reads an IF statement + * + * ```ebnf + * if ::= T_IF '(' expr ')' ':' ... + * ``` + */ + read_if: function() { + var result = this.node('if'), + body = null, + alternate = null, + shortForm = false, + test = null; + test = this.read_if_expr(); + + if (this.token === ':') { + shortForm = true; + this.next(); + body = this.node('block'); + var items = []; + while(this.token !== this.EOF && this.token !== this.tok.T_ENDIF) { + if (this.token === this.tok.T_ELSEIF) { + alternate = this.next().read_elseif_short(); + break; + } else if (this.token === this.tok.T_ELSE) { + alternate = this.next().read_else_short(); + break; + } + items.push(this.read_inner_statement()); + } + body = body(null, items); + this.expect(this.tok.T_ENDIF) && this.next(); + this.expectEndOfStatement(); + } else { + body = this.read_statement(); + /** + * ignore : if (..) { } /* *./ else { } + */ + this.ignoreComments(); + if (this.token === this.tok.T_ELSEIF) { + alternate = this.next().read_if(); + } else if (this.token === this.tok.T_ELSE) { + alternate = this.next().read_statement(); + } + } + return result(test, body, alternate, shortForm); + }, + /** + * reads an if expression : '(' expr ')' + */ + read_if_expr: function() { + this.expect('(') && this.next(); + var result = this.read_expr(); + this.expect(')') && this.next(); + return result; + }, + /** + * reads an elseif (expr): statements + */ + read_elseif_short: function() { + var result = this.node('if'), + alternate = null, + test = null, + body = null, + items = []; + test = this.read_if_expr(); + if (this.expect(':')) this.next(); + body = this.node('block'); + while(this.token != this.EOF && this.token !== this.tok.T_ENDIF) { + if (this.token === this.tok.T_ELSEIF) { + alternate = this.next().read_elseif_short(); + break; + } else if (this.token === this.tok.T_ELSE) { + alternate = this.next().read_else_short(); + break; + } + items.push(this.read_inner_statement()); + } + body = body(null, items); + return result(test, body, alternate, true); + }, + /** + * + */ + read_else_short: function() { + if (this.expect(':')) this.next(); + var body = this.node('block'), items = []; + while(this.token != this.EOF && this.token !== this.tok.T_ENDIF) { + items.push(this.read_inner_statement()); + } + return body(null, items); + } +}; + +},{}],108:[function(require,module,exports){ +/*! + * Copyright (C) 2017 Glayzzle (BSD3 License) + * @authors https://github.com/glayzzle/php-parser/graphs/contributors + * @url http://glayzzle.com + */ +"use strict"; +module.exports = { + /** + * Reads a while statement + * ```ebnf + * while ::= T_WHILE (statement | ':' inner_statement_list T_ENDWHILE ';') + * ``` + * @see https://github.com/php/php-src/blob/master/Zend/zend_language_parser.y#L587 + * @return {While} + */ + read_while: function() { + var result = this.node('while'), + test = null, + body = null, + shortForm = false + ; + if (this.expect('(')) this.next(); + test = this.read_expr(); + if (this.expect(')')) this.next(); + if (this.token === ':') { + shortForm = true; + body = this.read_short_form(this.tok.T_ENDWHILE); + } else { + body = this.read_statement(); + } + return result(test, body, shortForm); + } + /** + * Reads a do / while loop + * ```ebnf + * do ::= T_DO statement T_WHILE '(' expr ')' ';' + * ``` + * @see https://github.com/php/php-src/blob/master/Zend/zend_language_parser.y#L423 + * @return {Do} + */ + ,read_do: function() { + var result = this.node('do'), + test = null, + body = null + ; + body = this.read_statement(); + if (this.ignoreComments().expect(this.tok.T_WHILE)) { + if (this.next().expect('(')) this.next(); + test = this.read_expr(); + if (this.expect(')')) this.next(); + if (this.expect(';')) this.next(); + } + return result(test, body); + } + /** + * Read a for incremental loop + * ```ebnf + * for ::= T_FOR '(' for_exprs ';' for_exprs ';' for_exprs ')' for_statement + * for_statement ::= statement | ':' inner_statement_list T_ENDFOR ';' + * for_exprs ::= expr? (',' expr)* + * ``` + * @see https://github.com/php/php-src/blob/master/Zend/zend_language_parser.y#L425 + * @return {For} + */ + ,read_for: function() { + var result = this.node('for'), + init = [], + test = [], + increment = [], + body = null, + shortForm = false; + if (this.expect('(')) this.next(); + if (this.token !== ';') { + init = this.read_list(this.read_expr, ','); + if (this.expect(';')) this.next(); + } else { + this.next(); + } + if (this.token !== ';') { + test = this.read_list(this.read_expr, ','); + if (this.expect(';')) this.next(); + } else { + this.next(); + } + if (this.token !== ')') { + increment = this.read_list(this.read_expr, ','); + if (this.expect(')')) this.next(); + } else { + this.next(); + } + if (this.token === ':') { + shortForm = true; + body = this.read_short_form(this.tok.T_ENDFOR); + } else { + body = this.read_statement(); + } + return result(init, test, increment, body, shortForm); + } + /** + * Reads a foreach loop + * ```ebnf + * foreach ::= '(' expr T_AS foreach_variable (T_DOUBLE_ARROW foreach_variable)? ')' statement + * ``` + * @see https://github.com/php/php-src/blob/master/Zend/zend_language_parser.y#L438 + * @return {Foreach} + */ + ,read_foreach: function() { + var result = this.node('foreach'), + source = null, + key = null, + value = null, + body = null, + shortForm = false; + if (this.expect('(')) this.next(); + source = this.read_expr(); + if (this.ignoreComments().expect(this.tok.T_AS)) { + this.next(); + value = this.read_foreach_variable(); + if (this.token === this.tok.T_DOUBLE_ARROW) { + key = value; + value = this.next().read_foreach_variable(); + } + } + + if (this.expect(')')) this.next(); + + if (this.token === ':') { + shortForm = true; + body = this.read_short_form(this.tok.T_ENDFOREACH); + } else { + body = this.read_statement(); + } + return result(source, key, value, body, shortForm); + } + /** + * Reads a foreach variable statement + * ```ebnf + * foreach_variable = variable | + * T_LIST '(' assignment_list ')' | + * '[' array_pair_list ']' + * ``` + * @see https://github.com/php/php-src/blob/master/Zend/zend_language_parser.y#L544 + * @return {Expression} + */ + ,read_foreach_variable: function() { + if (this.token === this.tok.T_LIST) { + var result = this.node('list'); + if (this.next().expect('(')) this.next(); + var assignList = this.read_assignment_list(); + if (this.expect(')')) this.next(); + return result(assignList); + } else if (this.token === '[' || this.token === this.tok.T_ARRAY) { + return this.read_array(); + } else { + return this.read_variable(false, false, false); + } + } +}; + +},{}],109:[function(require,module,exports){ +/*! + * Copyright (C) 2017 Glayzzle (BSD3 License) + * @authors https://github.com/glayzzle/php-parser/graphs/contributors + * @url http://glayzzle.com + */ + +module.exports = { + /** + * ```ebnf + * start ::= (namespace | top_statement)* + * ``` + */ + read_start: function() { + if (this.token == this.tok.T_NAMESPACE) { + return this.read_namespace(); + } else { + return this.read_top_statement(); + } + } +}; + +},{}],110:[function(require,module,exports){ +/*! + * Copyright (C) 2017 Glayzzle (BSD3 License) + * @authors https://github.com/glayzzle/php-parser/graphs/contributors + * @url http://glayzzle.com + */ +"use strict"; + +module.exports = { + /** + * Reads a namespace declaration block + * ```ebnf + * namespace ::= T_NAMESPACE namespace_name? '{' + * top_statements + * '}' + * | T_NAMESPACE namespace_name ';' top_statements + * ``` + * @see http://php.net/manual/en/language.namespaces.php + * @return {Namespace} + */ + read_namespace: function() { + var result = this.node('namespace'); + this.expect(this.tok.T_NAMESPACE) && this.next(); + if (this.token == '{') { + this.currentNamespace = ['']; + var body = this.nextWithComments().read_top_statements(); + this.expect('}') && this.nextWithComments(); + return result([''], body, true); + } else { + var name = this.read_namespace_name(); + if (this.token == ';') { + this.currentNamespace = name; + var body = this.nextWithComments().read_top_statements(); + this.expect(this.EOF); + return result(name.name, body, false); + } else if (this.token == '{') { + this.currentNamespace = name; + var body = this.nextWithComments().read_top_statements(); + this.expect('}') && this.nextWithComments(); + return result(name.name, body, true); + } else if (this.token === '(') { + // resolve ambuiguity between namespace & function call + name.resolution = this.ast.identifier.RELATIVE_NAME; + name.name = name.name.substring(1); + return this.node('call')( + name, this.read_function_argument_list() + ); + } else { + this.error(['{', ';']); + // graceful mode : + this.currentNamespace = name; + var body = this.read_top_statements(); + this.expect(this.EOF); + return result(name, body, false); + } + } + } + /** + * Reads a namespace name + * ```ebnf + * namespace_name ::= T_NS_SEPARATOR? (T_STRING T_NS_SEPARATOR)* T_STRING + * ``` + * @see http://php.net/manual/en/language.namespaces.rules.php + * @return {Identifier} + */ + ,read_namespace_name: function() { + var result = this.node('identifier'), relative = false; + if (this.token === this.tok.T_NAMESPACE) { + this.next().expect(this.tok.T_NS_SEPARATOR) && this.next(); + relative = true + } + return result( + this.read_list(this.tok.T_STRING, this.tok.T_NS_SEPARATOR, true), + relative + ); + } + /** + * Reads a use statement + * ```ebnf + * use_statement ::= T_USE + * use_type? use_declarations | + * use_type use_statement '{' use_declarations '}' | + * use_statement '{' use_declarations(=>typed) '}' + * ';' + * ``` + * @see http://php.net/manual/en/language.namespaces.importing.php + * @return {UseGroup} + */ + ,read_use_statement: function() { + var result = this.node('usegroup'), + type = null, + items = [], + name = null + ; + this.expect(this.tok.T_USE) && this.next(); + type = this.read_use_type(); + items.push(this.read_use_declaration(false)); + if (this.token === ',') { + items = items.concat(this.next().read_use_declarations(false)); + } else if (this.token === '{') { + name = items[0].name.name; + items = this.next().read_use_declarations(type === null); + this.expect('}') && this.next(); + } + this.expect(';') && this.nextWithComments(); + return result(name, type, items); + } + /** + * Reads a use declaration + * ```ebnf + * use_declaration ::= use_type? namespace_name use_alias + * ``` + * @see https://github.com/php/php-src/blob/master/Zend/zend_language_parser.y#L380 + * @return {UseItem} + */ + ,read_use_declaration: function(typed) { + var result = this.node('useitem'), type = null; + if (typed) type = this.read_use_type(); + var name = this.read_namespace_name(); + var alias = this.read_use_alias(); + return result(name.name, alias, type); + } + /** + * Reads a list of use declarations + * ```ebnf + * use_declarations ::= use_declaration (',' use_declaration)* + * ``` + * @see https://github.com/php/php-src/blob/master/Zend/zend_language_parser.y#L380 + * @return {UseItem[]} + */ + ,read_use_declarations: function(typed) { + var result = [this.read_use_declaration(typed)]; + while(this.token === ',') { + result.push(this.next().read_use_declaration(typed)); + } + return result; + } + /** + * Reads a use statement + * ```ebnf + * use_alias ::= (T_AS T_STRING)? + * ``` + * @return {String|null} + */ + ,read_use_alias: function() { + var result = null; + if (this.token === this.tok.T_AS) { + if (this.next().expect(this.tok.T_STRING)) { + result = this.text(); + this.next(); + } + } + return result; + } + /** + * Reads the namespace type declaration + * ```ebnf + * use_type ::= (T_FUNCTION | T_CONST)? + * ``` + * @see https://github.com/php/php-src/blob/master/Zend/zend_language_parser.y#L335 + * @return {String|null} Possible values : function, const + */ + ,read_use_type: function() { + if (this.token === this.tok.T_FUNCTION) { + this.next(); + return this.ast.useitem.TYPE_FUNCTION; + } else if (this.token === this.tok.T_CONST) { + this.next(); + return this.ast.useitem.TYPE_CONST; + } + return null; + } +}; + +},{}],111:[function(require,module,exports){ +/*! + * Copyright (C) 2017 Glayzzle (BSD3 License) + * @authors https://github.com/glayzzle/php-parser/graphs/contributors + * @url http://glayzzle.com + */ + +var specialChar = { + '\\r': "\r", + '\\n': "\n", + '\\t': "\t", + '\\v': String.fromCharCode(11), + '\\e': String.fromCharCode(27), + '\\f': String.fromCharCode(12), + "\\\\": "\\", + '\\$': "$", + '\\"': '"', + '\\\'': "'" +}; + +module.exports = { + /** + * Unescape special chars + */ + resolve_special_chars: function(text) { + return text.replace( + /\\[rntvef"'\\\$]/g, + function(seq) { + return specialChar[seq]; + } + ); + }, + /** + * ```ebnf + * scalar ::= T_MAGIC_CONST + * | T_LNUMBER | T_DNUMBER + * | T_START_HEREDOC T_ENCAPSED_AND_WHITESPACE? T_END_HEREDOC + * | '"' encaps_list '"' + * | T_START_HEREDOC encaps_list T_END_HEREDOC + * | namespace_name (T_DOUBLE_COLON T_STRING)? + * ``` + */ + read_scalar: function() { + if (this.is('T_MAGIC_CONST')) { + return this.get_magic_constant(); + } else { + switch(this.token) { + + // TEXTS + case this.tok.T_CONSTANT_ENCAPSED_STRING: + var value = this.node('string'); + var text = this.text(); + var isDoubleQuote = text[0] === '"'; + text = text.substring(1, text.length - 1); + this.next(); + value = value(isDoubleQuote, this.resolve_special_chars(text)); + if (this.token === this.tok.T_DOUBLE_COLON) { + // https://github.com/php/php-src/blob/master/Zend/zend_language_parser.y#L1151 + return this.read_static_getter(value); + } else { + // dirrect string + return value; + } + case this.tok.T_START_HEREDOC: + if (this.lexer.curCondition === 'ST_NOWDOC') { + var node = this.node('nowdoc'); + var value = this.next().text(); + // strip the last line return char + var lastCh = value[value.length-1]; + if (lastCh === '\n') { + if (value[value.length-2] === '\r') { + // windows style + value = value.substring(0, value.length - 2); + } else { + // linux style + value = value.substring(0, value.length - 1); + } + } else if (lastCh === '\r') { + // mac style + value = value.substring(0, value.length - 1); + } + this.expect(this.tok.T_ENCAPSED_AND_WHITESPACE) && this.next(); + node = node(value, this.lexer.heredoc_label); + this.expect(this.tok.T_END_HEREDOC) && this.next(); + return node; + } else { + return this.next().read_encapsed_string( + this.tok.T_END_HEREDOC + ); + } + + case '"': + return this.next().read_encapsed_string('"'); + + case 'b"': + case 'B"': + var node = this.node('cast'); + var what = this.next().read_encapsed_string('"'); + return node('binary', what); + + // NUMERIC + case this.tok.T_LNUMBER: // long + case this.tok.T_DNUMBER: // double + var result = this.node('number'); + var value = this.text(); + this.next(); + result = result(value); + return result; + + // ARRAYS + case this.tok.T_ARRAY: // array parser + case '[': // short array format + return this.read_array(); + default: + var err = this.error('SCALAR'); + // graceful mode : ignore token & return error node + this.next(); + return err; + } + } + } + /** + * Handles the dereferencing + */ + ,read_dereferencable: function(expr) { + var result; + var node = this.node('offsetlookup'); + if (this.token === '[') { + var offset = this.next().read_expr(); + if (this.expect(']')) this.next(); + result = node(expr, offset); + } else if (this.token === this.tok.T_DOLLAR_OPEN_CURLY_BRACES) { + var offset = this.read_encapsed_string_item(); + result = node(expr, offset); + } + return result; + } + /** + * Reads and extracts an encapsed item + * ```ebnf + * encapsed_string_item ::= T_ENCAPSED_AND_WHITESPACE + * | T_DOLLAR_OPEN_CURLY_BRACES expr '}' + * | T_DOLLAR_OPEN_CURLY_BRACES T_STRING_VARNAME '}' + * | T_DOLLAR_OPEN_CURLY_BRACES T_STRING_VARNAME '[' expr ']' '}' + * | T_CURLY_OPEN variable '}' + * | variable + * | variable '[' expr ']' + * | variable T_OBJECT_OPERATOR T_STRING + * ``` + * @return {String|Variable|Expr|Lookup} + * @see https://github.com/php/php-src/blob/master/Zend/zend_language_parser.y#L1219 + */ + ,read_encapsed_string_item: function() { + var result = this.node(); + + // plain text + // https://github.com/php/php-src/blob/master/Zend/zend_language_parser.y#L1222 + if (this.token === this.tok.T_ENCAPSED_AND_WHITESPACE) { + var text = this.text(); + this.next(); + result = result( + 'string', false, this.resolve_special_chars(text) + ); + } + + // dynamic variable name + // https://github.com/php/php-src/blob/master/Zend/zend_language_parser.y#L1239 + else if (this.token === this.tok.T_DOLLAR_OPEN_CURLY_BRACES) { + var name = null; + if (this.next().token === this.tok.T_STRING_VARNAME) { + var varName = this.text(); + name = this.node('variable'); + this.next(); + name = name(varName, false); + // check if lookup an offset + // https://github.com/php/php-src/blob/master/Zend/zend_language_parser.y#L1243 + if (this.token === '[') { + var node = this.node('offsetlookup'); + var offset = this.next().read_expr(); + this.expect(']') && this.next(); + name = node(name, offset); + } + } else { + name = this.read_expr(); + } + this.expect('}') && this.next(); + result = result('variable', name, false); + } + + // expression + // https://github.com/php/php-src/blob/master/Zend/zend_language_parser.y#L1246 + else if (this.token === this.tok.T_CURLY_OPEN) { + result = this.next().read_variable(false, false, false); + this.expect('}') && this.next(); + } + + // plain variable + // https://github.com/php/php-src/blob/master/Zend/zend_language_parser.y#L1231 + else if (this.token === this.tok.T_VARIABLE) { + result = this.read_simple_variable(false); + + // https://github.com/php/php-src/blob/master/Zend/zend_language_parser.y#L1233 + if (this.token === '[') { + var node = this.node('offsetlookup'); + var offset = this.next().read_encaps_var_offset(); + this.expect(']') && this.next(); + result = node(result, offset); + } + + // https://github.com/php/php-src/blob/master/Zend/zend_language_parser.y#L1236 + if (this.token === this.tok.T_OBJECT_OPERATOR) { + var node = this.node('propertylookup'); + var what = this.node('constref'); + this.next().expect(this.tok.T_STRING); + var name = this.text(); + this.next(); + result = node(result, what(name)); + } + + // error / fallback + } else { + this.expect(this.tok.T_ENCAPSED_AND_WHITESPACE); + var value = this.text(); + this.next(); + // consider it as string + result = result('string', false, value); + } + + return result; + } + /** + * Reads an encapsed string + */ + ,read_encapsed_string: function(expect) { + var node = this.node('encapsed'), value = [], type = null; + + if (expect === '`') { + type = this.ast.encapsed.TYPE_SHELL; + } else if (expect === '"') { + type = this.ast.encapsed.TYPE_STRING; + } else { + type = this.ast.encapsed.TYPE_HEREDOC; + } + + // reading encapsed parts + while(this.token !== expect && this.token !== this.EOF) { + value.push(this.read_encapsed_string_item()); + } + + this.expect(expect) && this.next(); + node = node(value, type); + + if (expect === this.tok.T_END_HEREDOC) { + node.label = this.lexer.heredoc_label; + } + return node; + } + /** + * Constant token + */ + ,get_magic_constant: function() { + var result = this.node('magic'); + var name = this.text(); + this.next(); + return result(name); + } +}; + +},{}],112:[function(require,module,exports){ +/*! + * Copyright (C) 2017 Glayzzle (BSD3 License) + * @authors https://github.com/glayzzle/php-parser/graphs/contributors + * @url http://glayzzle.com + */ +module.exports = { + /** + * reading a list of top statements (helper for top_statement*) + * ```ebnf + * top_statements ::= top_statement* + * ``` + */ + read_top_statements: function() { + var result = []; + while(this.token !== this.EOF && this.token !== '}') { + var statement = this.read_top_statement(); + if (statement) { + if (Array.isArray(statement)) { + result = result.concat(statement); + } else { + result.push(statement); + } + } + } + return result; + } + /** + * reading a top statement + * ```ebnf + * top_statement ::= + * namespace | function | class + * | interface | trait + * | use_statements | const_list + * | statement + * ``` + */ + ,read_top_statement: function() { + switch(this.token) { + case this.tok.T_FUNCTION: + return this.read_function(false, false); + // optional flags + case this.tok.T_ABSTRACT: + case this.tok.T_FINAL: + var flag = this.read_class_scope(); + if (this.token === this.tok.T_CLASS) { + return this.read_class(flag); + } else { + this.error(this.tok.T_CLASS); + this.next(); + return null; + } + case this.tok.T_CLASS: + return this.read_class([0, 0, 0]); + case this.tok.T_INTERFACE: + return this.read_interface(); + case this.tok.T_TRAIT: + return this.read_trait(); + case this.tok.T_USE: + return this.read_use_statement(); + case this.tok.T_CONST: + return this.next().read_const_list(); + case this.tok.T_NAMESPACE: + return this.read_namespace(); + case this.tok.T_HALT_COMPILER: + var result = this.node('halt'); + if (this.next().expect('(')) this.next(); + if (this.expect(')')) this.next(); + this.expect(';'); + this.lexer.done = true; + return result(this.lexer._input.substring( + this.lexer.offset + )); + default: + return this.read_statement(); + } + } + /** + * reads a list of simple inner statements (helper for inner_statement*) + * ```ebnf + * inner_statements ::= inner_statement* + * ``` + */ + ,read_inner_statements: function() { + var result = []; + while(this.token != this.EOF && this.token !== '}') { + var statement = this.read_inner_statement(); + if (statement) { + if (Array.isArray(statement)) { + result = result.concat(statement); + } else { + result.push(statement); + } + } + } + return result; + } + /** + * Reads a list of constants declaration + * ```ebnf + * const_list ::= T_CONST T_STRING '=' expr (',' T_STRING '=' expr)* ';' + * ``` + */ + ,read_const_list: function() { + var result = this.read_list(function() { + this.expect(this.tok.T_STRING); + var result = this.node('constant'); + var name = this.text(); + if (this.next().expect('=')) { + return result(name, this.next().read_expr()); + } else { + // fallback + return result(name, null); + } + }, ',', false); + this.expectEndOfStatement(); + return result; + } + /** + * Reads a list of constants declaration + * ```ebnf + * declare_list ::= T_STRING '=' expr (',' T_STRING '=' expr)* + * ``` + * @retrurn {Object} + */ + ,read_declare_list: function() { + var result = {}; + while(this.token != this.EOF && this.token !== ')') { + this.expect(this.tok.T_STRING); + var name = this.text().toLowerCase(); + if (this.next().expect('=')) { + result[name] = this.next().read_expr(); + } else { + result[name] = null; + } + if (this.token !== ',') break; + this.next(); + } + return result; + } + /** + * reads a simple inner statement + * ```ebnf + * inner_statement ::= '{' inner_statements '}' | token + * ``` + */ + ,read_inner_statement: function() { + switch(this.token) { + case this.tok.T_FUNCTION: + return this.read_function(false, false); + // optional flags + case this.tok.T_ABSTRACT: + case this.tok.T_FINAL: + var flag = this.read_class_scope(); + if (this.token === this.tok.T_CLASS) { + return this.read_class(flag); + } else { + this.error(this.tok.T_CLASS); + // graceful mode : ignore token & go next + this.next(); + return null; + } + case this.tok.T_CLASS: + return this.read_class([0, 0, 0]); + case this.tok.T_INTERFACE: + return this.read_interface(); + case this.tok.T_TRAIT: + return this.read_trait(); + case this.tok.T_HALT_COMPILER: + this.raiseError( + '__HALT_COMPILER() can only be used from the outermost scope' + ); + // fallback : returns a node but does not stop the parsing + var node = this.node('halt'); + this.next().expect('(') && this.next(); + this.expect(')') && this.next(); + node = node(this.lexer._input.substring( + this.lexer.offset + )); + this.expect(';') && this.next(); + return node; + default: + return this.read_statement(); + } + } + /** + * Reads statements + */ + ,read_statement: function() { + + switch(this.token) { + + case '{': return this.read_code_block(false); + + case this.tok.T_IF: return this.next().read_if(); + + case this.tok.T_SWITCH: return this.read_switch(); + + case this.tok.T_FOR: return this.next().read_for(); + + case this.tok.T_FOREACH: return this.next().read_foreach(); + + case this.tok.T_WHILE: return this.next().read_while(); + + case this.tok.T_DO: return this.next().read_do(); + + case this.tok.T_COMMENT: return this.read_comment(); + + case this.tok.T_DOC_COMMENT: return this.read_doc_comment(); + + case this.tok.T_RETURN: + var result = this.node('return'), expr = null; + if (!this.next().is('EOS')) { + expr = this.read_expr(); + } + this.expectEndOfStatement(); + return result(expr); + + // https://github.com/php/php-src/blob/master/Zend/zend_language_parser.y#L429 + case this.tok.T_BREAK: + case this.tok.T_CONTINUE: + var result = this.node( + this.token === this.tok.T_CONTINUE ? 'continue' : 'break' + ), level = null; + this.next(); // look ahead + if (this.token !== ';' && this.token !== this.tok.T_CLOSE_TAG) { + level = this.read_expr(); + } + this.expectEndOfStatement(); + return result(level); + + case this.tok.T_GLOBAL: + var result = this.node('global'); + var items = this.next().read_list(this.read_simple_variable, ','); + this.expectEndOfStatement(); + return result(items); + + case this.tok.T_STATIC: + var current = [this.token, this.lexer.getState()]; + var result = this.node('static'); + if (this.next().token === this.tok.T_DOUBLE_COLON) { + // static keyword for a class + this.lexer.tokens.push(current); + var expr = this.next().read_expr(); + this.expect(';') && this.nextWithComments(); + return expr; + } + var items = this.read_variable_declarations(); + this.expectEndOfStatement(); + return result(items); + + case this.tok.T_ECHO: + var result = this.node('echo'); + var args = this.next().read_list(this.read_expr, ','); + this.expectEndOfStatement(); + return result(args); + + case this.tok.T_INLINE_HTML: + var result = this.node('inline'), value = this.text(); + this.next(); + return result(value); + + case this.tok.T_UNSET: + var result = this.node('unset'); + this.next().expect('(') && this.next(); + var items = this.read_list(this.read_variable, ','); + this.expect(')') && this.next(); + this.expect(';') && this.nextWithComments(); + return result(items); + + case this.tok.T_DECLARE: + var result = this.node('declare'), + what, + body = [], + mode; + this.next().expect('(') && this.next(); + what = this.read_declare_list(); + this.expect(')') && this.next(); + if (this.token === ':') { + this.nextWithComments(); + while(this.token != this.EOF && this.token !== this.tok.T_ENDDECLARE) { + // @todo : check declare_statement from php / not valid + body.push(this.read_top_statement()); + } + this.expect(this.tok.T_ENDDECLARE) && this.next(); + this.expectEndOfStatement(); + mode = this.ast.declare.MODE_SHORT; + } else if (this.token === '{') { + this.nextWithComments(); + while(this.token != this.EOF && this.token !== '}') { + // @todo : check declare_statement from php / not valid + body.push(this.read_top_statement()); + } + this.expect('}') && this.next(); + mode = this.ast.declare.MODE_BLOCK; + } else { + this.expect(';') && this.nextWithComments(); + while(this.token != this.EOF && this.token !== this.tok.T_DECLARE) { + // @todo : check declare_statement from php / not valid + body.push(this.read_top_statement()); + } + mode = this.ast.declare.MODE_NONE; + } + return result(what, body, mode); + + case this.tok.T_TRY: + return this.read_try(); + + case this.tok.T_THROW: + var result = this.node('throw'); + var expr = this.next().read_expr(); + this.expectEndOfStatement(); + return result(expr); + + case ';': // ignore this (extra ponctuation) + case this.tok.T_CLOSE_TAG: // empty tag + this.next(); + return null; + + case this.tok.T_STRING: + var current = [this.token, this.lexer.getState()]; + var label = this.text(); + if (this.next().token === ':') { + var result = this.node('label'); + this.next(); + return result(label); + } else { + // default fallback expr + this.lexer.tokens.push(current); + var expr = this.next().read_expr(); + this.expect([';', this.tok.T_CLOSE_TAG]) && this.nextWithComments(); + return expr; + } + + case this.tok.T_GOTO: + var result = this.node('goto'), label = null; + if (this.next().expect(this.tok.T_STRING)) { + label = this.text(); + this.next().expectEndOfStatement(); + } + return result(label); + + default: // default fallback expr + var expr = this.read_expr(); + this.expectEndOfStatement(); + return expr; + } + } + /** + * ```ebnf + * code_block ::= '{' (inner_statements | top_statements) '}' + * ``` + */ + ,read_code_block: function(top) { + var result = this.node('block'); + this.expect('{') && this.nextWithComments(); + var body = top ? + this.read_top_statements() + : this.read_inner_statements() + ; + this.expect('}') && this.nextWithComments(); + return result(null, body); + } +}; + +},{}],113:[function(require,module,exports){ +/*! + * Copyright (C) 2017 Glayzzle (BSD3 License) + * @authors https://github.com/glayzzle/php-parser/graphs/contributors + * @url http://glayzzle.com + */ +"use strict"; + +module.exports = { + /** + * Reads a switch statement + * ```ebnf + * switch ::= T_SWITCH '(' expr ')' switch_case_list + * ``` + * @return {Switch} + * @see http://php.net/manual/en/control-structures.switch.php + */ + read_switch: function() { + this.expect(this.tok.T_SWITCH) && this.next(); + var result = this.node('switch'), test, body, shortForm; + this.expect('(') && this.next(); + test = this.read_expr(); + this.expect(')') && this.next(); + shortForm = (this.token === ':'); + body = this.read_switch_case_list(); + return result(test, body, shortForm); + } + /** + * ```ebnf + * switch_case_list ::= '{' ';'? case_list* '}' | ':' ';'? case_list* T_ENDSWITCH ';' + * ``` + * @see https://github.com/php/php-src/blob/master/Zend/zend_language_parser.y#L566 + */ + ,read_switch_case_list: function() { + // DETECT SWITCH MODE + var expect = null, + result = this.node('block'), + items = []; + if (this.token === '{') { + expect = '}'; + } else if (this.token === ':') { + expect = this.tok.T_ENDSWITCH; + } else { + this.expect(['{', ':']); + } + // OPTIONNAL ';' + // https://github.com/php/php-src/blob/master/Zend/zend_language_parser.y#L570 + if (this.next().token === ';') { + this.next(); + } + // IGNORE THE CLOSE TAG TOKEN WITH SHORT MODE + if (this.token === this.tok.T_CLOSE_TAG) { + this.next(); + } + // EXTRACTING CASES + while(this.token !== this.EOF && this.token !== expect) { + items.push( this.read_case_list(expect) ); + } + // CHECK END TOKEN + this.expect(expect) && this.next(); + if (expect === this.tok.T_ENDSWITCH) { + this.expectEndOfStatement(); + } + return result(null, items); + } + /** + * ```ebnf + * case_list ::= ((T_CASE expr) | T_DEFAULT) (':' | ';') inner_statement* + * ``` + */ + ,read_case_list: function(stopToken) { + var result = this.node('case'), test = null, body = null, items = []; + if (this.token === this.tok.T_CASE) { + test = this.next().read_expr(); + } else if (this.token === this.tok.T_DEFAULT) { + // the defaut entry - no condition + this.next(); + } else { + this.expect([this.tok.T_CASE, this.tok.T_DEFAULT]); + } + this.expect([':', ';']) && this.next(); + body = this.node('block'); + while( + this.token != this.EOF + && this.token !== stopToken + && this.token !== this.tok.T_CASE + && this.token !== this.tok.T_DEFAULT + ) { + items.push(this.read_inner_statement()); + } + return result( + test, items.length > 0 ? body(null, items) : null + ); + } +}; + +},{}],114:[function(require,module,exports){ +/*! + * Copyright (C) 2017 Glayzzle (BSD3 License) + * @authors https://github.com/glayzzle/php-parser/graphs/contributors + * @url http://glayzzle.com + */ +module.exports = { + /** + * ```ebnf + * try ::= T_TRY '{' inner_statement* '}' + * ( + * T_CATCH '(' namespace_name variable ')' '{' inner_statement* '}' + * )* + * (T_FINALLY '{' inner_statement* '}')? + * ``` + * @see https://github.com/php/php-src/blob/master/Zend/zend_language_parser.y#L448 + * @return {Try} + */ + read_try: function() { + this.expect(this.tok.T_TRY); + var result = this.node('try'), + always = null, + body, + catches = [] + ; + body = this.next().read_statement(); + // https://github.com/php/php-src/blob/master/Zend/zend_language_parser.y#L455 + while(this.ignoreComments().token === this.tok.T_CATCH) { + var item = this.node('catch'), what = [], variable = null; + this.next().expect('(') && this.next(); + what = this.read_list( + this.read_namespace_name, '|', false + ); + variable = this.read_variable(true, false, false); + this.expect(')'); + catches.push( + item(this.next().read_statement(), what, variable) + ); + } + if (this.token === this.tok.T_FINALLY) { + always = this.next().read_statement(); + } + return result(body, catches, always); + } +}; + +},{}],115:[function(require,module,exports){ +/*! + * Defines a list of helper functions for parsing + * Copyright (C) 2017 Glayzzle (BSD3 License) + * @authors https://github.com/glayzzle/php-parser/graphs/contributors + * @url http://glayzzle.com + */ +"use strict"; + +module.exports = { + /** + * Reads a short form of tokens + * @param {Number} token - The ending token + * @return {Block} + */ + read_short_form: function(token) { + var body = this.node('block'), items = []; + if (this.expect(':')) this.next(); + while(this.token != this.EOF && this.token !== token) { + items.push(this.read_inner_statement()); + } + if (this.expect(token)) this.next(); + this.expectEndOfStatement(); + return body(null, items); + } + + /** + * Helper : reads a list of tokens / sample : T_STRING ',' T_STRING ... + * ```ebnf + * list ::= separator? ( item separator )* item + * ``` + */ + ,read_list: function(item, separator, preserveFirstSeparator) { + var result = []; + + if (this.token == separator) { + if (preserveFirstSeparator) result.push(''); + this.next(); + } + + if (typeof (item) === "function") { + do { + result.push(item.apply(this, [])); + if (this.token != separator) { + break; + } + } while(this.next().token != this.EOF); + } else { + if (this.expect(item)) { + result.push(this.text()); + } else { + return []; + } + while (this.next().token != this.EOF) { + if (this.token != separator) break; + // trim current separator & check item + if (this.next().token != item) break; + result.push(this.text()); + } + } + return result; + } + + /** + * Reads a list of names separated by a comma + * + * ```ebnf + * name_list ::= namespace (',' namespace)* + * ``` + * + * Sample code : + * ```php + * func()->property // chained calls + * ``` + */ + read_variable: function(read_only, encapsed, byref) { + var result; + + // check the byref flag + if (!byref && this.token === '&') { + byref = true; + this.next(); + } + + // reads the entry point + if (this.is([this.tok.T_VARIABLE, '$'])) { + result = this.read_reference_variable(encapsed, byref); + } else if (this.is([this.tok.T_NS_SEPARATOR, this.tok.T_STRING, this.tok.T_NAMESPACE])) { + result = this.node(); + var name = this.read_namespace_name(); + if ( + this.token != this.tok.T_DOUBLE_COLON + && this.token != '(' + ) { + // @see parser.js line 130 : resolves a conflict with scalar + var literal = name.name.toLowerCase(); + if (literal === 'true') { + result = result('boolean', true); + } else if (literal === 'false') { + result = result('boolean', false); + } else { + // @todo null keyword ? + result = result('constref', name); + } + } else { + result = name; + } + } else if (this.token === this.tok.T_STATIC) { + result = this.node('constref'); + this.next(); + result = result('static'); + } else { + this.expect('VARIABLE'); + } + + // static mode + if (this.token === this.tok.T_DOUBLE_COLON) { + result = this.read_static_getter(result, encapsed); + } + + return this.recursive_variable_chain_scan(result, read_only, encapsed); + } + + // resolves a static call + ,read_static_getter: function(what, encapsed) { + var result = this.node('staticlookup'); + var offset = null; + if (this.next().is([this.tok.T_VARIABLE, '$'])) { + offset = this.read_reference_variable(encapsed, false); + } else if ( + this.token === this.tok.T_STRING + || this.token === this.tok.T_CLASS + ) { + offset = this.node('constref'); + var name = this.text(); + this.next(); + offset = offset(name); + } else { + this.error([this.tok.T_VARIABLE, this.tok.T_STRING]); + // graceful mode : set getter as error node and continue + offset = this.node('constref'); + var name = this.text(); + this.next(); + offset = offset(name); + } + return result(what, offset); + } + + ,recursive_variable_chain_scan: function(result, read_only, encapsed) { + recursive_scan_loop: + while(this.token != this.EOF) { + switch(this.token) { + case '(': + if (read_only) { + // @fixme : add more informations & test + return result; + } else { + result = this.node('call')( + result, this.read_function_argument_list() + ); + } + break; + case '[': + var node = this.node('offsetlookup'); + this.next(); + var offset = false; + if (encapsed) { + offset = this.read_encaps_var_offset(); + this.expect(']') && this.next(); + } else { + // callable_variable : https://github.com/php/php-src/blob/493524454d66adde84e00d249d607ecd540de99f/Zend/zend_language_parser.y#L1122 + if (this.token !== ']') { + offset = this.read_expr(); + this.expect(']') && this.next(); + } else { + this.next(); + } + } + result = node(result, offset); + break; + case this.tok.T_OBJECT_OPERATOR: + var node = this.node('propertylookup'); + var what = null; + switch(this.next().token) { + case this.tok.T_STRING: + what = this.node('constref'); + var name = this.text(); + this.next(); + what = what(name); + if (this.token === this.tok.T_VARIABLE) { + var inner = this.node('variable'); + name = this.text().substring(1); + this.next(); + what = this.node('encapsed')( + [what, inner(name, false)], + 'offset' + ); + if (what.loc && what.value[0].loc) { + what.loc.start = what.value[0].loc.start; + } + } else if (this.token === '{') { + var expr = this.next().read_expr(); + this.expect('}') && this.next(); + what = this.node('encapsed')( + [what, expr], + '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'); + var name = this.text().substring(1); + this.next(); + what = what(name, false); + break; + case '$': + this.next().expect(['{', this.tok.T_VARIABLE]); + if (this.token === '{') { + // $obj->${$varname} + what = this.next().read_expr(); + this.expect('}') && this.next(); + } else { + // $obj->$$varname + what = this.read_expr(); + } + break; + case '{': + what = this.next().read_expr(); + this.expect('}') && this.next(); + break; + default: + this.error([this.tok.T_STRING, this.tok.T_VARIABLE]); + // graceful mode : set what as error mode & continue + what = this.node('constref'); + var name = this.text(); + this.next(); + what = what(name); + break; + } + result = node(result, what); + break; + default: + break recursive_scan_loop; + } + } + return result; + } + /** + * https://github.com/php/php-src/blob/493524454d66adde84e00d249d607ecd540de99f/Zend/zend_language_parser.y#L1231 + */ + ,read_encaps_var_offset: function() { + var offset = this.node(); + if (this.token === this.tok.T_STRING) { + var text = this.text(); + var isDblQuote = text[0] === '"'; + text = text.substring(1, text.length - 1); + this.next(); + offset = offset( + 'string', isDblQuote, this.resolve_special_chars(text) + ); + } else if (this.token === this.tok.T_NUM_STRING) { + var num = this.text(); + this.next(); + offset = offset('number', num); + } else if (this.token === this.tok.T_VARIABLE) { + var name = this.text().substring(1); + this.next(); + offset = offset('variable', name, false); + } else { + this.expect([ + this.tok.T_STRING, + this.tok.T_NUM_STRING, + this.tok.T_VARIABLE + ]); + // fallback : consider as text + var text = this.text(); + this.next(); + offset = offset('string', false, text); + } + return offset; + } + /** + * ```ebnf + * reference_variable ::= simple_variable ('[' OFFSET ']')* | '{' EXPR '}' + * ``` + * + * $foo[123]; // foo is an array ==> gets its entry + * $foo{1}; // foo is a string ==> get the 2nd char offset + * ${'foo'}[123]; // get the dynamic var $foo + * $foo[123]{1}; // gets the 2nd char from the 123 array entry + * + */ + ,read_reference_variable: function(encapsed, byref) { + var result = this.read_simple_variable(byref); + while(this.token != this.EOF) { + var node = this.node(); + if (this.token == '[') { + var offset = null; + if (encapsed) { + offset = this.next().read_encaps_var_offset(); + } else { + offset = this.next().token === ']' ? null : this.read_dim_offset(); + } + this.expect(']') && this.next(); + result = node('offsetlookup', result, offset); + } else if (this.token == '{' && !encapsed) { + var offset = this.next().read_expr(); + this.expect('}') && this.next(); + result = node('offsetlookup', result, offset); + } else break; + } + return result; + } + /** + * ```ebnf + * simple_variable ::= T_VARIABLE | '$' '{' expr '}' | '$' simple_variable + * ``` + */ + ,read_simple_variable: function(byref) { + var result = this.node('variable'); + if (this.expect([this.tok.T_VARIABLE, '$']) && this.token === this.tok.T_VARIABLE) { + // plain variable name + var name = this.text().substring(1); + this.next(); + result = result(name, byref); + } else { + if (this.token === '$') this.next(); + // dynamic variable name + switch(this.token) { + case '{': + var expr = this.next().read_expr(); + this.expect('}') && this.next(); + result = result(expr, byref); + break; + case '$': // $$$var + result = result(this.read_simple_variable(false), byref); + break; + case this.tok.T_VARIABLE: // $$var + var name = this.text().substring(1); + var node = this.node('variable'); + this.next(); + result = result(node(name, false), byref); + break; + default: + this.error(['{', '$', this.tok.T_VARIABLE]); + // graceful mode + var name = this.text(); + this.next(); + result = result(name, byref); + } + } + return result; + } +}; + +},{}],117:[function(require,module,exports){ +/*! + * Copyright (C) 2017 Glayzzle (BSD3 License) + * @authors https://github.com/glayzzle/php-parser/graphs/contributors + * @url http://glayzzle.com + */ + +// exports token index +module.exports = { + values: { + 101: 'T_HALT_COMPILER', + 102: 'T_USE', + 103: 'T_ENCAPSED_AND_WHITESPACE', + 104: 'T_OBJECT_OPERATOR', + 105: 'T_STRING', + 106: 'T_DOLLAR_OPEN_CURLY_BRACES', + 107: 'T_STRING_VARNAME', + 108: 'T_CURLY_OPEN', + 109: 'T_NUM_STRING', + 110: 'T_ISSET', + 111: 'T_EMPTY', + 112: 'T_INCLUDE', + 113: 'T_INCLUDE_ONCE', + 114: 'T_EVAL', + 115: 'T_REQUIRE', + 116: 'T_REQUIRE_ONCE', + 117: 'T_NAMESPACE', + 118: 'T_NS_SEPARATOR', + 119: 'T_AS', + 120: 'T_IF', + 121: 'T_ENDIF', + 122: 'T_WHILE', + 123: 'T_DO', + 124: 'T_FOR', + 125: 'T_SWITCH', + 126: 'T_BREAK', + 127: 'T_CONTINUE', + 128: 'T_RETURN', + 129: 'T_GLOBAL', + 130: 'T_STATIC', + 131: 'T_ECHO', + 132: 'T_INLINE_HTML', + 133: 'T_UNSET', + 134: 'T_FOREACH', + 135: 'T_DECLARE', + 136: 'T_TRY', + 137: 'T_THROW', + 138: 'T_GOTO', + 139: 'T_FINALLY', + 140: 'T_CATCH', + 141: 'T_ENDDECLARE', + 142: 'T_LIST', + 143: 'T_CLONE', + 144: 'T_PLUS_EQUAL', + 145: 'T_MINUS_EQUAL', + 146: 'T_MUL_EQUAL', + 147: 'T_DIV_EQUAL', + 148: 'T_CONCAT_EQUAL', + 149: 'T_MOD_EQUAL', + 150: 'T_AND_EQUAL', + 151: 'T_OR_EQUAL', + 152: 'T_XOR_EQUAL', + 153: 'T_SL_EQUAL', + 154: 'T_SR_EQUAL', + 155: 'T_INC', + 156: 'T_DEC', + 157: 'T_BOOLEAN_OR', + 158: 'T_BOOLEAN_AND', + 159: 'T_LOGICAL_OR', + 160: 'T_LOGICAL_AND', + 161: 'T_LOGICAL_XOR', + 162: 'T_SL', + 163: 'T_SR', + 164: 'T_IS_IDENTICAL', + 165: 'T_IS_NOT_IDENTICAL', + 166: 'T_IS_EQUAL', + 167: 'T_IS_NOT_EQUAL', + 168: 'T_IS_SMALLER_OR_EQUAL', + 169: 'T_IS_GREATER_OR_EQUAL', + 170: 'T_INSTANCEOF', + 171: 'T_INT_CAST', + 172: 'T_DOUBLE_CAST', + 173: 'T_STRING_CAST', + 174: 'T_ARRAY_CAST', + 175: 'T_OBJECT_CAST', + 176: 'T_BOOL_CAST', + 177: 'T_UNSET_CAST', + 178: 'T_EXIT', + 179: 'T_PRINT', + 180: 'T_YIELD', + 181: 'T_YIELD_FROM', + 182: 'T_FUNCTION', + 183: 'T_DOUBLE_ARROW', + 184: 'T_DOUBLE_COLON', + 185: 'T_ARRAY', + 186: 'T_CALLABLE', + 187: 'T_CLASS', + 188: 'T_ABSTRACT', + 189: 'T_TRAIT', + 190: 'T_FINAL', + 191: 'T_EXTENDS', + 192: 'T_INTERFACE', + 193: 'T_IMPLEMENTS', + 194: 'T_VAR', + 195: 'T_PUBLIC', + 196: 'T_PROTECTED', + 197: 'T_PRIVATE', + 198: 'T_CONST', + 199: 'T_NEW', + 200: 'T_INSTEADOF', + 201: 'T_ELSEIF', + 202: 'T_ELSE', + 203: 'T_ENDSWITCH', + 204: 'T_CASE', + 205: 'T_DEFAULT', + 206: 'T_ENDFOR', + 207: 'T_ENDFOREACH', + 208: 'T_ENDWHILE', + 209: 'T_CONSTANT_ENCAPSED_STRING', + 210: 'T_LNUMBER', + 211: 'T_DNUMBER', + 212: 'T_LINE', + 213: 'T_FILE', + 214: 'T_DIR', + 215: 'T_TRAIT_C', + 216: 'T_METHOD_C', + 217: 'T_FUNC_C', + 218: 'T_NS_C', + 219: 'T_START_HEREDOC', + 220: 'T_END_HEREDOC', + 221: 'T_CLASS_C', + 222: 'T_VARIABLE', + 223: 'T_OPEN_TAG', + 224: 'T_OPEN_TAG_WITH_ECHO', + 225: 'T_CLOSE_TAG', + 226: 'T_WHITESPACE', + 227: 'T_COMMENT', + 228: 'T_DOC_COMMENT', + 229: 'T_ELLIPSIS', + 230: 'T_COALESCE', + 231: 'T_POW', + 232: 'T_POW_EQUAL', + 233: 'T_SPACESHIP' + }, + names: { + T_HALT_COMPILER: 101, + T_USE: 102, + T_ENCAPSED_AND_WHITESPACE: 103, + T_OBJECT_OPERATOR: 104, + T_STRING: 105, + T_DOLLAR_OPEN_CURLY_BRACES: 106, + T_STRING_VARNAME: 107, + T_CURLY_OPEN: 108, + T_NUM_STRING: 109, + T_ISSET: 110, + T_EMPTY: 111, + T_INCLUDE: 112, + T_INCLUDE_ONCE: 113, + T_EVAL: 114, + T_REQUIRE: 115, + T_REQUIRE_ONCE: 116, + T_NAMESPACE: 117, + T_NS_SEPARATOR: 118, + T_AS: 119, + T_IF: 120, + T_ENDIF: 121, + T_WHILE: 122, + T_DO: 123, + T_FOR: 124, + T_SWITCH: 125, + T_BREAK: 126, + T_CONTINUE: 127, + T_RETURN: 128, + T_GLOBAL: 129, + T_STATIC: 130, + T_ECHO: 131, + T_INLINE_HTML: 132, + T_UNSET: 133, + T_FOREACH: 134, + T_DECLARE: 135, + T_TRY: 136, + T_THROW: 137, + T_GOTO: 138, + T_FINALLY: 139, + T_CATCH: 140, + T_ENDDECLARE: 141, + T_LIST: 142, + T_CLONE: 143, + T_PLUS_EQUAL: 144, + T_MINUS_EQUAL: 145, + T_MUL_EQUAL: 146, + T_DIV_EQUAL: 147, + T_CONCAT_EQUAL: 148, + T_MOD_EQUAL: 149, + T_AND_EQUAL: 150, + T_OR_EQUAL: 151, + T_XOR_EQUAL: 152, + T_SL_EQUAL: 153, + T_SR_EQUAL: 154, + T_INC: 155, + T_DEC: 156, + T_BOOLEAN_OR: 157, + T_BOOLEAN_AND: 158, + T_LOGICAL_OR: 159, + T_LOGICAL_AND: 160, + T_LOGICAL_XOR: 161, + T_SL: 162, + T_SR: 163, + T_IS_IDENTICAL: 164, + T_IS_NOT_IDENTICAL: 165, + T_IS_EQUAL: 166, + T_IS_NOT_EQUAL: 167, + T_IS_SMALLER_OR_EQUAL: 168, + T_IS_GREATER_OR_EQUAL: 169, + T_INSTANCEOF: 170, + T_INT_CAST: 171, + T_DOUBLE_CAST: 172, + T_STRING_CAST: 173, + T_ARRAY_CAST: 174, + T_OBJECT_CAST: 175, + T_BOOL_CAST: 176, + T_UNSET_CAST: 177, + T_EXIT: 178, + T_PRINT: 179, + T_YIELD: 180, + T_YIELD_FROM: 181, + T_FUNCTION: 182, + T_DOUBLE_ARROW: 183, + T_DOUBLE_COLON: 184, + T_ARRAY: 185, + T_CALLABLE: 186, + T_CLASS: 187, + T_ABSTRACT: 188, + T_TRAIT: 189, + T_FINAL: 190, + T_EXTENDS: 191, + T_INTERFACE: 192, + T_IMPLEMENTS: 193, + T_VAR: 194, + T_PUBLIC: 195, + T_PROTECTED: 196, + T_PRIVATE: 197, + T_CONST: 198, + T_NEW: 199, + T_INSTEADOF: 200, + T_ELSEIF: 201, + T_ELSE: 202, + T_ENDSWITCH: 203, + T_CASE: 204, + T_DEFAULT: 205, + T_ENDFOR: 206, + T_ENDFOREACH: 207, + T_ENDWHILE: 208, + T_CONSTANT_ENCAPSED_STRING: 209, + T_LNUMBER: 210, + T_DNUMBER: 211, + T_LINE: 212, + T_FILE: 213, + T_DIR: 214, + T_TRAIT_C: 215, + T_METHOD_C: 216, + T_FUNC_C: 217, + T_NS_C: 218, + T_START_HEREDOC: 219, + T_END_HEREDOC: 220, + T_CLASS_C: 221, + T_VARIABLE: 222, + T_OPEN_TAG: 223, + T_OPEN_TAG_WITH_ECHO: 224, + T_CLOSE_TAG: 225, + T_WHITESPACE: 226, + T_COMMENT: 227, + T_DOC_COMMENT: 228, + T_ELLIPSIS: 229, + T_COALESCE: 230, + T_POW: 231, + T_POW_EQUAL: 232, + T_SPACESHIP: 233 + } +}; + +},{}]},{},[91])(91) +}); \ No newline at end of file