Skip to content

Instantly share code, notes, and snippets.

@rvanvelzen
Created October 18, 2012 13:53
Show Gist options
  • Save rvanvelzen/3911967 to your computer and use it in GitHub Desktop.
Save rvanvelzen/3911967 to your computer and use it in GitHub Desktop.
<?php
// Acorn was written by Marijn Haverbeke and released under an MIT
// license. The Unicode regexps (for identifiers and whitespace) were
// taken from [Esprima](http://esprima.org) by Ariya Hidayat.
//
// This port was done by Richard van Velzen
namespace JavaScript;
class Exception extends \Exception { }
class Parser {
const VERSION = '0.0.1';
const NON_ASCII_WHITESPACE = '~[\x{1680}\x{180e}\x{2000}-\x{200a}\x{202f}\x{205f}\x{3000}\x{feff}]~u';
const NON_ASCII_IDENTIFIER_START = '~[\xaa\xb5\xba\xc0-\xd6\xd8-\xf6\xf8-\u02c1\u02c6-\u02d1\u02e0-\u02e4\u02ec\u02ee\u0370-\u0374\u0376\u0377\u037a-\u037d\u0386\u0388-\u038a\u038c\u038e-\u03a1\u03a3-\u03f5\u03f7-\u0481\u048a-\u0527\u0531-\u0556\u0559\u0561-\u0587\u05d0-\u05ea\u05f0-\u05f2\u0620-\u064a\u066e\u066f\u0671-\u06d3\u06d5\u06e5\u06e6\u06ee\u06ef\u06fa-\u06fc\u06ff\u0710\u0712-\u072f\u074d-\u07a5\u07b1\u07ca-\u07ea\u07f4\u07f5\u07fa\u0800-\u0815\u081a\u0824\u0828\u0840-\u0858\u08a0\u08a2-\u08ac\u0904-\u0939\u093d\u0950\u0958-\u0961\u0971-\u0977\u0979-\u097f\u0985-\u098c\u098f\u0990\u0993-\u09a8\u09aa-\u09b0\u09b2\u09b6-\u09b9\u09bd\u09ce\u09dc\u09dd\u09df-\u09e1\u09f0\u09f1\u0a05-\u0a0a\u0a0f\u0a10\u0a13-\u0a28\u0a2a-\u0a30\u0a32\u0a33\u0a35\u0a36\u0a38\u0a39\u0a59-\u0a5c\u0a5e\u0a72-\u0a74\u0a85-\u0a8d\u0a8f-\u0a91\u0a93-\u0aa8\u0aaa-\u0ab0\u0ab2\u0ab3\u0ab5-\u0ab9\u0abd\u0ad0\u0ae0\u0ae1\u0b05-\u0b0c\u0b0f\u0b10\u0b13-\u0b28\u0b2a-\u0b30\u0b32\u0b33\u0b35-\u0b39\u0b3d\u0b5c\u0b5d\u0b5f-\u0b61\u0b71\u0b83\u0b85-\u0b8a\u0b8e-\u0b90\u0b92-\u0b95\u0b99\u0b9a\u0b9c\u0b9e\u0b9f\u0ba3\u0ba4\u0ba8-\u0baa\u0bae-\u0bb9\u0bd0\u0c05-\u0c0c\u0c0e-\u0c10\u0c12-\u0c28\u0c2a-\u0c33\u0c35-\u0c39\u0c3d\u0c58\u0c59\u0c60\u0c61\u0c85-\u0c8c\u0c8e-\u0c90\u0c92-\u0ca8\u0caa-\u0cb3\u0cb5-\u0cb9\u0cbd\u0cde\u0ce0\u0ce1\u0cf1\u0cf2\u0d05-\u0d0c\u0d0e-\u0d10\u0d12-\u0d3a\u0d3d\u0d4e\u0d60\u0d61\u0d7a-\u0d7f\u0d85-\u0d96\u0d9a-\u0db1\u0db3-\u0dbb\u0dbd\u0dc0-\u0dc6\u0e01-\u0e30\u0e32\u0e33\u0e40-\u0e46\u0e81\u0e82\u0e84\u0e87\u0e88\u0e8a\u0e8d\u0e94-\u0e97\u0e99-\u0e9f\u0ea1-\u0ea3\u0ea5\u0ea7\u0eaa\u0eab\u0ead-\u0eb0\u0eb2\u0eb3\u0ebd\u0ec0-\u0ec4\u0ec6\u0edc-\u0edf\u0f00\u0f40-\u0f47\u0f49-\u0f6c\u0f88-\u0f8c\u1000-\u102a\u103f\u1050-\u1055\u105a-\u105d\u1061\u1065\u1066\u106e-\u1070\u1075-\u1081\u108e\u10a0-\u10c5\u10c7\u10cd\u10d0-\u10fa\u10fc-\u1248\u124a-\u124d\u1250-\u1256\u1258\u125a-\u125d\u1260-\u1288\u128a-\u128d\u1290-\u12b0\u12b2-\u12b5\u12b8-\u12be\u12c0\u12c2-\u12c5\u12c8-\u12d6\u12d8-\u1310\u1312-\u1315\u1318-\u135a\u1380-\u138f\u13a0-\u13f4\u1401-\u166c\u166f-\u167f\u1681-\u169a\u16a0-\u16ea\u16ee-\u16f0\u1700-\u170c\u170e-\u1711\u1720-\u1731\u1740-\u1751\u1760-\u176c\u176e-\u1770\u1780-\u17b3\u17d7\u17dc\u1820-\u1877\u1880-\u18a8\u18aa\u18b0-\u18f5\u1900-\u191c\u1950-\u196d\u1970-\u1974\u1980-\u19ab\u19c1-\u19c7\u1a00-\u1a16\u1a20-\u1a54\u1aa7\u1b05-\u1b33\u1b45-\u1b4b\u1b83-\u1ba0\u1bae\u1baf\u1bba-\u1be5\u1c00-\u1c23\u1c4d-\u1c4f\u1c5a-\u1c7d\u1ce9-\u1cec\u1cee-\u1cf1\u1cf5\u1cf6\u1d00-\u1dbf\u1e00-\u1f15\u1f18-\u1f1d\u1f20-\u1f45\u1f48-\u1f4d\u1f50-\u1f57\u1f59\u1f5b\u1f5d\u1f5f-\u1f7d\u1f80-\u1fb4\u1fb6-\u1fbc\u1fbe\u1fc2-\u1fc4\u1fc6-\u1fcc\u1fd0-\u1fd3\u1fd6-\u1fdb\u1fe0-\u1fec\u1ff2-\u1ff4\u1ff6-\u1ffc\u2071\u207f\u2090-\u209c\u2102\u2107\u210a-\u2113\u2115\u2119-\u211d\u2124\u2126\u2128\u212a-\u212d\u212f-\u2139\u213c-\u213f\u2145-\u2149\u214e\u2160-\u2188\u2c00-\u2c2e\u2c30-\u2c5e\u2c60-\u2ce4\u2ceb-\u2cee\u2cf2\u2cf3\u2d00-\u2d25\u2d27\u2d2d\u2d30-\u2d67\u2d6f\u2d80-\u2d96\u2da0-\u2da6\u2da8-\u2dae\u2db0-\u2db6\u2db8-\u2dbe\u2dc0-\u2dc6\u2dc8-\u2dce\u2dd0-\u2dd6\u2dd8-\u2dde\u2e2f\u3005-\u3007\u3021-\u3029\u3031-\u3035\u3038-\u303c\u3041-\u3096\u309d-\u309f\u30a1-\u30fa\u30fc-\u30ff\u3105-\u312d\u3131-\u318e\u31a0-\u31ba\u31f0-\u31ff\u3400-\u4db5\u4e00-\u9fcc\ua000-\ua48c\ua4d0-\ua4fd\ua500-\ua60c\ua610-\ua61f\ua62a\ua62b\ua640-\ua66e\ua67f-\ua697\ua6a0-\ua6ef\ua717-\ua71f\ua722-\ua788\ua78b-\ua78e\ua790-\ua793\ua7a0-\ua7aa\ua7f8-\ua801\ua803-\ua805\ua807-\ua80a\ua80c-\ua822\ua840-\ua873\ua882-\ua8b3\ua8f2-\ua8f7\ua8fb\ua90a-\ua925\ua930-\ua946\ua960-\ua97c\ua984-\ua9b2\ua9cf\uaa00-\uaa28\uaa40-\uaa42\uaa44-\uaa4b\uaa60-\uaa76\uaa7a\uaa80-\uaaaf\uaab1\uaab5\uaab6\uaab9-\uaabd\uaac0\uaac2\uaadb-\uaadd\uaae0-\uaaea\uaaf2-\uaaf4\uab01-\uab06\uab09-\uab0e\uab11-\uab16\uab20-\uab26\uab28-\uab2e\uabc0-\uabe2\uac00-\ud7a3\ud7b0-\ud7c6\ud7cb-\ud7fb\uf900-\ufa6d\ufa70-\ufad9\ufb00-\ufb06\ufb13-\ufb17\ufb1d\ufb1f-\ufb28\ufb2a-\ufb36\ufb38-\ufb3c\ufb3e\ufb40\ufb41\ufb43\ufb44\ufb46-\ufbb1\ufbd3-\ufd3d\ufd50-\ufd8f\ufd92-\ufdc7\ufdf0-\ufdfb\ufe70-\ufe74\ufe76-\ufefc\uff21-\uff3a\uff41-\uff5a\uff66-\uffbe\uffc2-\uffc7\uffca-\uffcf\uffd2-\uffd7\uffda-\uffdc]~u';
const NON_ASCII_IDENTIFIER = '~[\xaa\xb5\xba\xc0-\xd6\xd8-\xf6\xf8-\u02c1\u02c6-\u02d1\u02e0-\u02e4\u02ec\u02ee\u0370-\u0374\u0376\u0377\u037a-\u037d\u0386\u0388-\u038a\u038c\u038e-\u03a1\u03a3-\u03f5\u03f7-\u0481\u048a-\u0527\u0531-\u0556\u0559\u0561-\u0587\u05d0-\u05ea\u05f0-\u05f2\u0620-\u064a\u066e\u066f\u0671-\u06d3\u06d5\u06e5\u06e6\u06ee\u06ef\u06fa-\u06fc\u06ff\u0710\u0712-\u072f\u074d-\u07a5\u07b1\u07ca-\u07ea\u07f4\u07f5\u07fa\u0800-\u0815\u081a\u0824\u0828\u0840-\u0858\u08a0\u08a2-\u08ac\u0904-\u0939\u093d\u0950\u0958-\u0961\u0971-\u0977\u0979-\u097f\u0985-\u098c\u098f\u0990\u0993-\u09a8\u09aa-\u09b0\u09b2\u09b6-\u09b9\u09bd\u09ce\u09dc\u09dd\u09df-\u09e1\u09f0\u09f1\u0a05-\u0a0a\u0a0f\u0a10\u0a13-\u0a28\u0a2a-\u0a30\u0a32\u0a33\u0a35\u0a36\u0a38\u0a39\u0a59-\u0a5c\u0a5e\u0a72-\u0a74\u0a85-\u0a8d\u0a8f-\u0a91\u0a93-\u0aa8\u0aaa-\u0ab0\u0ab2\u0ab3\u0ab5-\u0ab9\u0abd\u0ad0\u0ae0\u0ae1\u0b05-\u0b0c\u0b0f\u0b10\u0b13-\u0b28\u0b2a-\u0b30\u0b32\u0b33\u0b35-\u0b39\u0b3d\u0b5c\u0b5d\u0b5f-\u0b61\u0b71\u0b83\u0b85-\u0b8a\u0b8e-\u0b90\u0b92-\u0b95\u0b99\u0b9a\u0b9c\u0b9e\u0b9f\u0ba3\u0ba4\u0ba8-\u0baa\u0bae-\u0bb9\u0bd0\u0c05-\u0c0c\u0c0e-\u0c10\u0c12-\u0c28\u0c2a-\u0c33\u0c35-\u0c39\u0c3d\u0c58\u0c59\u0c60\u0c61\u0c85-\u0c8c\u0c8e-\u0c90\u0c92-\u0ca8\u0caa-\u0cb3\u0cb5-\u0cb9\u0cbd\u0cde\u0ce0\u0ce1\u0cf1\u0cf2\u0d05-\u0d0c\u0d0e-\u0d10\u0d12-\u0d3a\u0d3d\u0d4e\u0d60\u0d61\u0d7a-\u0d7f\u0d85-\u0d96\u0d9a-\u0db1\u0db3-\u0dbb\u0dbd\u0dc0-\u0dc6\u0e01-\u0e30\u0e32\u0e33\u0e40-\u0e46\u0e81\u0e82\u0e84\u0e87\u0e88\u0e8a\u0e8d\u0e94-\u0e97\u0e99-\u0e9f\u0ea1-\u0ea3\u0ea5\u0ea7\u0eaa\u0eab\u0ead-\u0eb0\u0eb2\u0eb3\u0ebd\u0ec0-\u0ec4\u0ec6\u0edc-\u0edf\u0f00\u0f40-\u0f47\u0f49-\u0f6c\u0f88-\u0f8c\u1000-\u102a\u103f\u1050-\u1055\u105a-\u105d\u1061\u1065\u1066\u106e-\u1070\u1075-\u1081\u108e\u10a0-\u10c5\u10c7\u10cd\u10d0-\u10fa\u10fc-\u1248\u124a-\u124d\u1250-\u1256\u1258\u125a-\u125d\u1260-\u1288\u128a-\u128d\u1290-\u12b0\u12b2-\u12b5\u12b8-\u12be\u12c0\u12c2-\u12c5\u12c8-\u12d6\u12d8-\u1310\u1312-\u1315\u1318-\u135a\u1380-\u138f\u13a0-\u13f4\u1401-\u166c\u166f-\u167f\u1681-\u169a\u16a0-\u16ea\u16ee-\u16f0\u1700-\u170c\u170e-\u1711\u1720-\u1731\u1740-\u1751\u1760-\u176c\u176e-\u1770\u1780-\u17b3\u17d7\u17dc\u1820-\u1877\u1880-\u18a8\u18aa\u18b0-\u18f5\u1900-\u191c\u1950-\u196d\u1970-\u1974\u1980-\u19ab\u19c1-\u19c7\u1a00-\u1a16\u1a20-\u1a54\u1aa7\u1b05-\u1b33\u1b45-\u1b4b\u1b83-\u1ba0\u1bae\u1baf\u1bba-\u1be5\u1c00-\u1c23\u1c4d-\u1c4f\u1c5a-\u1c7d\u1ce9-\u1cec\u1cee-\u1cf1\u1cf5\u1cf6\u1d00-\u1dbf\u1e00-\u1f15\u1f18-\u1f1d\u1f20-\u1f45\u1f48-\u1f4d\u1f50-\u1f57\u1f59\u1f5b\u1f5d\u1f5f-\u1f7d\u1f80-\u1fb4\u1fb6-\u1fbc\u1fbe\u1fc2-\u1fc4\u1fc6-\u1fcc\u1fd0-\u1fd3\u1fd6-\u1fdb\u1fe0-\u1fec\u1ff2-\u1ff4\u1ff6-\u1ffc\u2071\u207f\u2090-\u209c\u2102\u2107\u210a-\u2113\u2115\u2119-\u211d\u2124\u2126\u2128\u212a-\u212d\u212f-\u2139\u213c-\u213f\u2145-\u2149\u214e\u2160-\u2188\u2c00-\u2c2e\u2c30-\u2c5e\u2c60-\u2ce4\u2ceb-\u2cee\u2cf2\u2cf3\u2d00-\u2d25\u2d27\u2d2d\u2d30-\u2d67\u2d6f\u2d80-\u2d96\u2da0-\u2da6\u2da8-\u2dae\u2db0-\u2db6\u2db8-\u2dbe\u2dc0-\u2dc6\u2dc8-\u2dce\u2dd0-\u2dd6\u2dd8-\u2dde\u2e2f\u3005-\u3007\u3021-\u3029\u3031-\u3035\u3038-\u303c\u3041-\u3096\u309d-\u309f\u30a1-\u30fa\u30fc-\u30ff\u3105-\u312d\u3131-\u318e\u31a0-\u31ba\u31f0-\u31ff\u3400-\u4db5\u4e00-\u9fcc\ua000-\ua48c\ua4d0-\ua4fd\ua500-\ua60c\ua610-\ua61f\ua62a\ua62b\ua640-\ua66e\ua67f-\ua697\ua6a0-\ua6ef\ua717-\ua71f\ua722-\ua788\ua78b-\ua78e\ua790-\ua793\ua7a0-\ua7aa\ua7f8-\ua801\ua803-\ua805\ua807-\ua80a\ua80c-\ua822\ua840-\ua873\ua882-\ua8b3\ua8f2-\ua8f7\ua8fb\ua90a-\ua925\ua930-\ua946\ua960-\ua97c\ua984-\ua9b2\ua9cf\uaa00-\uaa28\uaa40-\uaa42\uaa44-\uaa4b\uaa60-\uaa76\uaa7a\uaa80-\uaaaf\uaab1\uaab5\uaab6\uaab9-\uaabd\uaac0\uaac2\uaadb-\uaadd\uaae0-\uaaea\uaaf2-\uaaf4\uab01-\uab06\uab09-\uab0e\uab11-\uab16\uab20-\uab26\uab28-\uab2e\uabc0-\uabe2\uac00-\ud7a3\ud7b0-\ud7c6\ud7cb-\ud7fb\uf900-\ufa6d\ufa70-\ufad9\ufb00-\ufb06\ufb13-\ufb17\ufb1d\ufb1f-\ufb28\ufb2a-\ufb36\ufb38-\ufb3c\ufb3e\ufb40\ufb41\ufb43\ufb44\ufb46-\ufbb1\ufbd3-\ufd3d\ufd50-\ufd8f\ufd92-\ufdc7\ufdf0-\ufdfb\ufe70-\ufe74\ufe76-\ufefc\uff21-\uff3a\uff41-\uff5a\uff66-\uffbe\uffc2-\uffc7\uffca-\uffcf\uffd2-\uffd7\uffda-\uffdc\x{0371}-\x{0374}\x{0483}-\x{0487}\x{0591}-\x{05bd}\x{05bf}\x{05c1}\x{05c2}\x{05c4}\x{05c5}\x{05c7}\x{0610}-\x{061a}\x{0620}-\x{0649}\x{0672}-\x{06d3}\x{06e7}-\x{06e8}\x{06fb}-\x{06fc}\x{0730}-\x{074a}\x{0800}-\x{0814}\x{081b}-\x{0823}\x{0825}-\x{0827}\x{0829}-\x{082d}\x{0840}-\x{0857}\x{08e4}-\x{08fe}\x{0900}-\x{0903}\x{093a}-\x{093c}\x{093e}-\x{094f}\x{0951}-\x{0957}\x{0962}-\x{0963}\x{0966}-\x{096f}\x{0981}-\x{0983}\x{09bc}\x{09be}-\x{09c4}\x{09c7}\x{09c8}\x{09d7}\x{09df}-\x{09e0}\x{0a01}-\x{0a03}\x{0a3c}\x{0a3e}-\x{0a42}\x{0a47}\x{0a48}\x{0a4b}-\x{0a4d}\x{0a51}\x{0a66}-\x{0a71}\x{0a75}\x{0a81}-\x{0a83}\x{0abc}\x{0abe}-\x{0ac5}\x{0ac7}-\x{0ac9}\x{0acb}-\x{0acd}\x{0ae2}-\x{0ae3}\x{0ae6}-\x{0aef}\x{0b01}-\x{0b03}\x{0b3c}\x{0b3e}-\x{0b44}\x{0b47}\x{0b48}\x{0b4b}-\x{0b4d}\x{0b56}\x{0b57}\x{0b5f}-\x{0b60}\x{0b66}-\x{0b6f}\x{0b82}\x{0bbe}-\x{0bc2}\x{0bc6}-\x{0bc8}\x{0bca}-\x{0bcd}\x{0bd7}\x{0be6}-\x{0bef}\x{0c01}-\x{0c03}\x{0c46}-\x{0c48}\x{0c4a}-\x{0c4d}\x{0c55}\x{0c56}\x{0c62}-\x{0c63}\x{0c66}-\x{0c6f}\x{0c82}\x{0c83}\x{0cbc}\x{0cbe}-\x{0cc4}\x{0cc6}-\x{0cc8}\x{0cca}-\x{0ccd}\x{0cd5}\x{0cd6}\x{0ce2}-\x{0ce3}\x{0ce6}-\x{0cef}\x{0d02}\x{0d03}\x{0d46}-\x{0d48}\x{0d57}\x{0d62}-\x{0d63}\x{0d66}-\x{0d6f}\x{0d82}\x{0d83}\x{0dca}\x{0dcf}-\x{0dd4}\x{0dd6}\x{0dd8}-\x{0ddf}\x{0df2}\x{0df3}\x{0e34}-\x{0e3a}\x{0e40}-\x{0e45}\x{0e50}-\x{0e59}\x{0eb4}-\x{0eb9}\x{0ec8}-\x{0ecd}\x{0ed0}-\x{0ed9}\x{0f18}\x{0f19}\x{0f20}-\x{0f29}\x{0f35}\x{0f37}\x{0f39}\x{0f41}-\x{0f47}\x{0f71}-\x{0f84}\x{0f86}-\x{0f87}\x{0f8d}-\x{0f97}\x{0f99}-\x{0fbc}\x{0fc6}\x{1000}-\x{1029}\x{1040}-\x{1049}\x{1067}-\x{106d}\x{1071}-\x{1074}\x{1082}-\x{108d}\x{108f}-\x{109d}\x{135d}-\x{135f}\x{170e}-\x{1710}\x{1720}-\x{1730}\x{1740}-\x{1750}\x{1772}\x{1773}\x{1780}-\x{17b2}\x{17dd}\x{17e0}-\x{17e9}\x{180b}-\x{180d}\x{1810}-\x{1819}\x{1920}-\x{192b}\x{1930}-\x{193b}\x{1951}-\x{196d}\x{19b0}-\x{19c0}\x{19c8}-\x{19c9}\x{19d0}-\x{19d9}\x{1a00}-\x{1a15}\x{1a20}-\x{1a53}\x{1a60}-\x{1a7c}\x{1a7f}-\x{1a89}\x{1a90}-\x{1a99}\x{1b46}-\x{1b4b}\x{1b50}-\x{1b59}\x{1b6b}-\x{1b73}\x{1bb0}-\x{1bb9}\x{1be6}-\x{1bf3}\x{1c00}-\x{1c22}\x{1c40}-\x{1c49}\x{1c5b}-\x{1c7d}\x{1cd0}-\x{1cd2}\x{1d00}-\x{1dbe}\x{1e01}-\x{1f15}\x{200c}\x{200d}\x{203f}\x{2040}\x{2054}\x{20d0}-\x{20dc}\x{20e1}\x{20e5}-\x{20f0}\x{2d81}-\x{2d96}\x{2de0}-\x{2dff}\x{3021}-\x{3028}\x{3099}\x{309a}\x{a640}-\x{a66d}\x{a674}-\x{a67d}\x{a69f}\x{a6f0}-\x{a6f1}\x{a7f8}-\x{a800}\x{a806}\x{a80b}\x{a823}-\x{a827}\x{a880}-\x{a881}\x{a8b4}-\x{a8c4}\x{a8d0}-\x{a8d9}\x{a8f3}-\x{a8f7}\x{a900}-\x{a909}\x{a926}-\x{a92d}\x{a930}-\x{a945}\x{a980}-\x{a983}\x{a9b3}-\x{a9c0}\x{aa00}-\x{aa27}\x{aa40}-\x{aa41}\x{aa4c}-\x{aa4d}\x{aa50}-\x{aa59}\x{aa7b}\x{aae0}-\x{aae9}\x{aaf2}-\x{aaf3}\x{abc0}-\x{abe1}\x{abec}\x{abed}\x{abf0}-\x{abf9}\x{fb20}-\x{fb28}\x{fe00}-\x{fe0f}\x{fe20}-\x{fe26}\x{fe33}\x{fe34}\x{fe4d}-\x{fe4f}\x{ff10}-\x{ff19}\x{ff3f}]~u';
const NEWLINE = '~[\r\n\x{2028}\x{2029}]~u';
const LINEBREAK = '~\r\n|[\n\r\x{2028}\x{2029}]~u';
protected $options = array();
protected $input = '';
protected $inputLen = 0;
protected $sourceFile;
protected static $defaultOptions = array(
// `ecmaVersion` indicates the ECMAScript version to parse. Must
// be either 3 or 5. This
// influences support for strict mode, the set of reserved words, and
// support for getters and setter.
'ecmaVersion' => 5,
// Turn on `strictSemicolons` to prevent the parser from doing
// automatic semicolon insertion.
'strictSemicolons' => false,
// When `allowTrailingCommas` is false, the parser will not allow
// trailing commas in array and object literals.
'allowTrailingCommas' => true,
// By default, reserved words are not enforced. Enable
// `forbidReserved` to enforce them.
'forbidReserved' => false,
// When `trackComments` is turned on, the parser will attach
// `commentsBefore` and `commentsAfter` properties to AST nodes
// holding arrays of strings. A single comment may appear in both
// a `commentsBefore` and `commentsAfter` array (of the nodes
// after and before it), but never twice in the before (or after)
// array of different nodes.
'trackComments' => false,
// When `locations` is on, `loc` properties holding objects with
// `start` and `end` properties in `{line, column}` form (with
// line being 1-based and column 0-based) will be attached to the
// nodes.
'locations' => false,
// Nodes have their start and end characters offsets recorded in
// `start` and `end` properties (directly on the node, rather than
// the `loc` object, which holds line/column data. To also add a
// [semi-standardized][range] `range` property holding a `[start,
// end]` array with the same numbers, set the `ranges` option to
// `true`.
//
// [range]: https://bugzilla.mozilla.org/show_bug.cgi?id=745678
'ranges' => false,
// It is possible to parse multiple files into a single AST by
// passing the tree produced by parsing the first file as
// `program` option in subsequent parses. This will add the
// toplevel forms of the parsed file to the `Program` (top) node
// of an existing parse tree.
'program' => null,
// When `location` is on, you can pass this to record the source
// file in every node's `loc` object.
'sourceFile' => null,
// When `strict` is true, the entire program will be parsed as if
// strict mode was on
'strict' => false
);
protected $tokPos = 0;
protected $tokStart, $tokEnd;
protected $tokStartLoc, $tokEndLoc;
protected $tokType, $tokVal;
protected $tokCommentsBefore, $tokCommentsAfter;
protected $tokRegexpAllowed, $tokComments;
protected $tokCurLine, $tokLineStart, $tokLineStartNext;
protected $lastStart, $lastEnd, $lastEndLoc;
protected $inFunction = false, $labels = array(), $strict = false;
protected $_num = array('type' => 'num');
protected $_regexp = array('type' => 'regexp');
protected $_string = array('type' => 'string');
protected $_name = array('type' => 'name');
protected $_eof = array('type' => 'eof');
protected $_break = array('keyword' => 'break');
protected $_case = array('keyword' => 'case', 'beforeExpr' => true);
protected $_catch = array('keyword' => 'catch');
protected $_continue = array('keyword' => 'continue');
protected $_debugger = array('keyword' => 'debugger');
protected $_default = array('keyword' => 'default');
protected $_do = array('keyword' => 'do', 'isLoop' => true);
protected $_else = array('keyword' => 'else', 'beforeExpr' => true);
protected $_finally = array('keyword' => 'finally');
protected $_for = array('keyword' => 'for', 'isLoop' => true);
protected $_function = array('keyword' => 'function');
protected $_if = array('keyword' => 'if');
protected $_return = array('keyword' => 'return', 'beforeExpr' => true);
protected $_switch = array('keyword' => 'switch');
protected $_throw = array('keyword' => 'throw', 'beforeExpr' => true);
protected $_try = array('keyword' => 'try');
protected $_var = array('keyword' => 'var');
protected $_while = array('keyword' => 'while', 'isLoop' => true);
protected $_this = array('keyword' => 'this');
protected $_with = array('keyword' => 'with');
protected $_new = array('keyword' => 'new', 'beforeExpr' => true);
protected $_null = array('keyword' => 'null', 'atomValue' => null);
protected $_true = array('keyword' => 'true', 'atomValue' => true);
protected $_false = array('keyword' => 'false', 'atomValue' => false);
protected $_in = array('keyword' => 'in', 'binop' => 7, 'beforeExpr' => true);
protected $keywordTypes = array(
'break' => array('keyword' => 'break'),
'case' => array('keyword' => 'case', 'beforeExpr' => true),
'catch' => array('keyword' => 'catch'),
'continue' => array('keyword' => 'continue'),
'debugger' => array('keyword' => 'debugger'),
'default' => array('keyword' => 'default'),
'do' => array('keyword' => 'do', 'isLoop' => true),
'else' => array('keyword' => 'else', 'beforeExpr' => true),
'finally' => array('keyword' => 'finally'),
'for' => array('keyword' => 'for', 'isLoop' => true),
'function' => array('keyword' => 'function'),
'if' => array('keyword' => 'if'),
'return' => array('keyword' => 'return', 'beforeExpr' => true),
'switch' => array('keyword' => 'switch'),
'throw' => array('keyword' => 'throw', 'beforeExpr' => true),
'try' => array('keyword' => 'try'),
'var' => array('keyword' => 'var'),
'while' => array('keyword' => 'while', 'isLoop' => true),
'with' => array('keyword' => 'with'),
'null' => array('keyword' => 'null', 'atomValue' => null),
'true' => array('keyword' => 'true', 'atomValue' => true),
'false' => array('keyword' => 'false', 'atomValue' => false),
'new' => array('keyword' => 'new', 'beforeExpr' => true),
'in' => array('keyword' => 'in', 'binop' => 7, 'beforeExpr' => true),
'instanceof' => array('keyword' => 'instanceof', 'binop' => 7),
'this' => array('keyword' => 'this'),
'typeof' => array('keyword' => 'typeof', 'prefix' => true),
'void' => array('keyword' => 'void', 'prefix' => true),
'delete' => array('keyword' => 'delete', 'prefix' => true)
);
protected $_bracketL = array('type' => '[', 'beforeExpr' => true);
protected $_bracketR = array('type' => ']', 'beforeExpr' => true);
protected $_braceL = array('type' => '{', 'beforeExpr' => true);
protected $_braceR = array('type' => '}');
protected $_parenL = array('type' => '(', 'beforeExpr' => true);
protected $_parenR = array('type' => ')');
protected $_comma = array('type' => ',', 'beforeExpr' => true);
protected $_semi = array('type' => ';', 'beforeExpr' => true);
protected $_colon = array('type' => ':', 'beforeExpr' => true);
protected $_dot = array('type' => '.');
protected $_question = array('type' => '?', 'beforeExpr' => true);
protected $_slash = array('type' => '/', 'binop' => 10, 'beforeExpr' => true);
protected $_eq = array('type' => '=', 'isAssign' => true, 'beforeExpr' => true);
protected $_assign = array('type' => 'assign', 'isAssign' => true, 'beforeExpr' => true);
protected $_plusmin = array('binop' => 9, 'prefix' => true, 'beforeExpr' => true);
protected $_incdec = array('postfix' => true, 'prefix' => true, 'isUpdate' => true);
protected $_prefix = array('type' => 'prefix', 'prefix' => true, 'beforeExpr' => true);
protected $_bin1 = array('binop' => 1, 'beforeExpr' => true);
protected $_bin2 = array('binop' => 2, 'beforeExpr' => true);
protected $_bin3 = array('binop' => 3, 'beforeExpr' => true);
protected $_bin4 = array('binop' => 4, 'beforeExpr' => true);
protected $_bin5 = array('binop' => 5, 'beforeExpr' => true);
protected $_bin6 = array('binop' => 6, 'beforeExpr' => true);
protected $_bin7 = array('binop' => 7, 'beforeExpr' => true);
protected $_bin8 = array('binop' => 8, 'beforeExpr' => true);
protected $_bin10 = array('binop' => 10, 'beforeExpr' => true);
protected $reservedWord3 = array(
'abstract' => true, 'boolean' => true, 'byte' => true, 'char' => true, 'class' => true,
'double' => true, 'enum' => true, 'export' => true, 'extends' => true, 'final' => true,
'float' => true, 'goto' => true, 'implements' => true, 'import' => true, 'int' => true,
'interface' => true, 'long' => true, 'native' => true, 'package' => true, 'private' => true,
'protected' => true, 'public' => true, 'short' => true, 'static' => true, 'super' => true,
'synchronized' => true, 'throws' => true, 'transient' => true, 'volatile' => true
);
protected function isReservedWord3($str) {
return isset($this->reservedWord3[$str]);
}
protected $reservedWord5 = array(
'class' => true, 'enum' => true, 'extends' => true,
'super' => true, 'const' => true, 'export' => true, 'import' => true
);
protected function isReservedWord5($str) {
return isset($this->reservedWord5[$str]);
}
protected $strictReservedWord = array(
'implements' => true, 'interface' => true, 'let' => true, 'package' => true,
'private' => true, 'protected' => true, 'public' => true, 'static' => true,
'yield' => true
);
protected function isStrictReservedWord($str) {
return isset($this->strictReservedWord[$str]);
}
protected $strictBadWord = array('eval' => true, 'arguments' => true);
protected function isStrictBadWord($str) {
return isset($this->strictBadWord[$str]);
}
protected $keyword = array(
'break' => true, 'case' => true, 'catch' => true, 'continue' => true, 'debugger' => true,
'default' => true, 'do' => true, 'else' => true, 'finally' => true, 'for' => true,
'function' => true, 'if' => true, 'return' => true, 'switch' => true, 'throw' => true,
'try' => true, 'var' => true, 'while' => true, 'with' => true, 'null' => true,
'true' => true, 'false' => true, 'instanceof' => true, 'typeof' => true,
'void' => true, 'delete' => true, 'new' => true, 'in' => true, 'this' => true
);
protected function isKeyword($str) {
return isset($this->keyword[$str]);
}
protected $containsEsc;
protected $lastFinishedNode;
protected $loopLabel;
protected $switchLabel;
public function parse($input, array $options = array()) {
$this->loopLabel = \JavaScript\AST\Node::get(array('kind' => 'loop'));
$this->switchLabel = \JavaScript\AST\Node::get(array('kind' => 'switch'));
$this->input = (string)$input;
$this->inputLen = strlen($this->input);
$this->options = $options;
foreach(self::$defaultOptions as $key => $value) {
if (!array_key_exists($key, $this->options)) {
$this->options[$key] = $value;
}
}
$this->strict = $this->options['strict'];
$this->sourceFile = $this->options['sourceFile'];
return $this->parseTopLevel($this->options['program']);
}
protected function getLineInfo($input, $offset) {
$piece = substr($input, 0, $offset);
$m = null;
$line = preg_match_all(self::LINEBREAK, $piece, $m) + 1;
$offset = strrpos($piece, "\n");
$column = strlen($piece) - ($offset === false ? 0 : $offset + 1);
return array(
'line' => $line,
'column' => $column
);
}
protected function raise($pos, $message) {
if (ctype_digit((string)$pos)) {
$pos = $this->getLineInfo($this->input, $pos);
}
$message .= ' (' . $pos['line'] . ':' . $pos['column'] . ')';
throw new Exception($message);
}
protected function nextLineStart() {
$m = null;
preg_match(self::LINEBREAK, $this->input, $m, PREG_OFFSET_CAPTURE, $this->tokLineStart);
if ($m && $m[0][1] === $this->tokLineStart) {
return $m[0][1] + strlen($m[0][0]);
}
return strlen($this->input) + 1;
}
protected function curLineLoc() {
while ($this->tokLineStartNext <= $this->tokPos) {
++$this->tokCurLine;
$this->tokLineStart = $this->tokLineStartNext;
$this->tokLineStartNext = $this->nextLineStart();
}
return array(
'line' => $this->tokCurLine,
'column' => ($this->tokPos - $this->tokLineStart)
);
}
protected function initTokenState() {
$this->tokCurLine = 1;
$this->tokPos = $this->tokLineStart = 0;
$this->tokLineStartNext = $this->nextLineStart();
$this->tokRegexpAllowed = true;
$this->tokComments = null;
$this->skipSpace();
}
protected function finishToken($type, $val = null) {
$this->tokEnd = $this->tokPos;
if (isset($this->options['locations'])) {
$this->tokEndLoc = $this->curLineLoc();
}
$this->tokType = $type;
$this->skipSpace();
$this->tokVal = $val;
$this->tokCommentsAfter = $this->tokComments;
$this->tokRegexpAllowed = isset($type['beforeExpr']);
}
protected function skipSpace() {
while ($this->tokPos < $this->inputLen) {
$ch = $this->input[$this->tokPos];
if ($ch === '/') {
$nextCh = $this->input[$this->tokPos + 1];
if ($nextCh === '*') {
$end = strpos($this->input, '*/', $this->tokPos += 2);
if ($end === false) {
$this->raise($this->tokPos - 2, 'Unterminated comment');
}
if ($this->options['trackComments']) {
if (!$this->tokComments) {
$this->tokComments = array();
}
$this->tokComments[] = substr($this->input, $this->tokPos, $end - $this->tokPos);
}
$this->tokPos = $end + 2;
} elseif ($nextCh === '/') {
$start = $this->tokPos;
$this->tokPos += 2;
while ($this->tokPos < $this->inputLen && $this->input[$this->tokPos] !== "\n") {
++$this->tokPos;
}
if ($this->options['trackComments']) {
if (!$this->tokComments) {
$this->tokComments = array();
}
$this->tokComments[] = substr($this->input, $start, $this->tokPos - $start);
}
} else {
break;
}
} elseif ($ch === ' ' || $ch === "\t" || $ch === "\n" || $ch === "\r" || $ch === "\f" ||
$ch === "\xa0" || $ch === "\x0b") {
++$this->tokPos;
} else {
break;
}
}
}
protected function readToken($forceRegexp = false) {
$this->tokStart = $this->tokPos;
$this->tokCommentsBefore = $this->tokComments;
if ($this->options['locations']) {
$this->tokStartLoc = $this->curLineLoc();
}
if ($forceRegexp) {
return $this->readRegexp();
}
if ($this->tokPos >= $this->inputLen) {
return $this->finishToken($this->_eof, 'EOF');
}
$code = ord($char = $this->input[$this->tokPos]);
if (($code >= 65 && $code <= 90) || ($code >= 97 && $code <= 122) ||
$code === 36 || $code === 95 || $code === 92 ||
($code >= 0xaa && preg_match(self::NON_ASCII_IDENTIFIER_START, $char))) {
return $this->readWord();
}
$next = $this->tokPos + 1 < $this->inputLen ? ord($this->input[$this->tokPos + 1]) : null;
switch ($code) {
case 46: // .
if ($next >= 48 && $next <= 57) {
return $this->readNumber($char);
}
++$this->tokPos;
return $this->finishToken($this->_dot, '.');
case 40: ++$this->tokPos; return $this->finishToken($this->_parenL, '(');
case 41: ++$this->tokPos; return $this->finishToken($this->_parenR, ')');
case 59: ++$this->tokPos; return $this->finishToken($this->_semi, ';');
case 44: ++$this->tokPos; return $this->finishToken($this->_comma, ',');
case 91: ++$this->tokPos; return $this->finishToken($this->_bracketL, '[');
case 93: ++$this->tokPos; return $this->finishToken($this->_bracketR, ']');
case 123: ++$this->tokPos; return $this->finishToken($this->_braceL, '{');
case 125: ++$this->tokPos; return $this->finishToken($this->_braceR, '}');
case 58: ++$this->tokPos; return $this->finishToken($this->_colon, ':');
case 63: ++$this->tokPos; return $this->finishToken($this->_question, '?');
case 48:
if ($next === 120 || $next === 88) {
return $this->readHexNumber();
}
case 49: case 50: case 51: case 52: case 53: case 54: case 55: case 56: case 57:
return $this->readNumber($char);
case 34: case 39:
return $this->readString($char);
case 47: // /
if ($this->tokRegexpAllowed) {
++$this->tokPos;
return $this->readRegexp();
}
if ($next === 61) {
return $this->finishOp($this->_assign, 2);
}
return $this->finishOp($this->_slash, 1);
case 37: case 42:
if ($next === 61) {
return $this->finishOp($this->_assign, 2);
}
return $this->finishOp($this->_bin10, 1);
case 124: case 38:
if ($next === $code) {
return $this->finishOp($code === 124 ? $this->_bin1 : $this->_bin2, 2);
} elseif ($next === 61) {
return $this->finishOp($this->_assign, 2);
}
return $this->finishOp($code === 124 ? $this->_bin3 : $this->_bin5, 1);
case 94:
if ($next === 61) {
return $this->finishOp($this->_assign, 2);
}
return $this->finishOp($this->_bin4, 1);
case 43: case 45:
if ($next === $code) {
return $this->finishOp($this->_incdec, 2);
} elseif ($next === 61) {
return $this->finishOp($this->_assign, 2);
}
return $this->finishOp($this->_plusmin, 1);
case 60: case 62: // '<>'
$size = 1;
if ($next === $code) {
$size = $code === 62 && ord($this->input[$this->tokPos + 2]) === 62 ? 3 : 2;
if (ord($this->input[$this->tokPos + $size]) === 61) {
return $this->finishOp($this->_assign, $size + 1);
}
return $this->finishOp($this->_bin8, $size);
}
if ($next === 61) {
$size = $this->input[$this->tokPos + 2] === '=' ? 3 : 2;
}
return $this->finishOp($this->_bin8, $size);
case 61: case 33:
if ($next === 61) {
return $this->finishOp($this->_bin6, ord($this->input[$this->tokPos + 2]) === 61 ? 3 : 2);
}
return $this->finishOp($code === 61 ? $this->_eq : $this->_prefix, 1);
case 126:
return $this->finishOp($this->_prefix, 1);
}
if ($char === '\\' || preg_match(self::NON_ASCII_IDENTIFIER_START, $char)) {
return $this->readWord();
}
$this->raise($this->tokPos, 'Unexpected character "' . $char . '"');
}
protected function finishOp($type, $size) {
$str = substr($this->input, $this->tokPos, $size);
$this->tokPos += $size;
$this->finishToken($type, $str);
}
protected function readRegexp() {
$start = $this->tokPos;
$inClass = false;
$escaped = false;
for (;;) {
if ($this->tokPos >= $this->inputLen) {
$this->raise($start, 'Unterminated regular expression');
}
$ch = $this->input[$this->tokPos];
if (preg_match(self::NEWLINE, $ch)) {
$this->raise($start, 'Unterminated regular expression');
}
if (!$escaped) {
if ($ch === '[') {
$inClass = true;
} elseif ($inClass && $ch === ']') {
$inClass = false;
} elseif ($ch === '/' && !$inClass) {
break;
}
$escaped = $ch === '\\';
} else {
$escaped = false;
}
++$this->tokPos;
}
$content = substr($this->input, $start, $this->tokPos - $start);
++$this->tokPos;
$mods = $this->readWord1();
if ($mods && !preg_match('~^[gmsiy]+$~', $mods)) {
$this->raise($start, 'Invalid regexp flag');
}
return $this->finishToken($this->_regexp, '/' . $content . '/' . $mods);
}
protected function readInt($radix = 10, $len = null) {
$start = $this->tokPos;
$total = 0;
while ($this->tokPos < $this->inputLen) {
$code = ord($this->input[$this->tokPos]);
if ($code >= 97) {
$val = $code - 97 + 10; // A
} elseif ($code >= 65) {
$val = $code - 65 + 10; // A
} elseif ($code >= 48 && $code <= 57) {
$val = $code - 48;
} else {
$val = INF;
}
if ($val >= $radix) {
break;
}
++$this->tokPos;
$total = $total * $radix + $val;
}
if ($this->tokPos === $start || $len !== null && $this->tokPos - $start !== $len) {
return null;
}
return $total;
}
protected function readHexNumber() {
$this->tokPos += 2; // 0x
$val = $this->readInt(16);
if ($val === null) {
$this->raise($this->tokStart + 2, 'Expected hexadecimal number');
}
$ch = $this->input[$this->tokPos];
if ((($ch >= 'a' && $ch <= 'z') || ($ch >= 'A' && $ch <= 'Z') ||
$ch === '$' || $ch === '_') ||
($ch >= "\xaa" && preg_match(self::NON_ASCII_IDENTIFIER_START, $ch))) {
$this->raise($this->tokPos, 'Identifier directly after number');
}
return $this->finishToken($this->_num, $val);
}
protected function readNumber($ch) {
$start = $this->tokPos;
$isFloat = $ch === '.';
if (!$isFloat && $this->readInt(10) === null) {
$this->raise($start, 'Invalid number');
}
if ($this->tokPos < $this->inputLen) {
if ($isFloat || $this->input[$this->tokPos] === '.') {
$next = $this->input[++$this->tokPos];
if ($next === '-' || $next === '+') {
++$this->tokPos;
}
if ($this->readInt(10) === null && $ch === '.') {
$this->raise($start, 'Invalid number');
}
$isFloat = true;
}
if (\strcasecmp($this->input[$this->tokPos], 'e') === 0) {
$next = $this->input[++$this->tokPos];
if ($next === '-' || $next === '+') {
++$this->tokPos;
}
if ($this->readInt(10) === null) {
$this->raise($start, 'Invalid number');
}
$isFloat = true;
}
$ch = $this->input[$this->tokPos];
if ((($ch >= 'a' && $ch <= 'z') || ($ch >= 'A' && $ch <= 'Z') ||
$ch === '$' || $ch === '_') ||
($ch >= "\xaa" && preg_match(self::NON_ASCII_IDENTIFIER_START, $ch))) {
$this->raise($this->tokPos, 'Identifier directly after number');
}
}
$str = \substr($this->input, $start, $this->tokPos - $start);
if ($isFloat) {
$val = eval('return ' . $str . ';');
} elseif ($ch !== '0' || strlen($str) === 1) {
$val = eval('return ' . $str . ';');
} elseif (\strtok($str, '89')) {
$this->raise($start, 'Invalid number');
} else {
$val = eval('return ' . $str . ';');
}
return $this->finishToken($this->_num, $val);
}
protected function readString($quote) {
++$this->tokPos;
$str = '';
for (;;) {
if ($this->tokPos >= $this->inputLen) {
$this->raise($this->tokStart, 'Unterminated string constant');
}
$ch = $this->input[$this->tokPos];
if ($ch === $quote) {
++$this->tokPos;
return $this->finishToken($this->_string, $str);
}
if ($ch === '\\') {
$ch = $this->input[++$this->tokPos];
$m = null;
$octal = \preg_match('~^[0-7]+~', \substr($this->input, $this->tokPos, 3), $m);
if ($octal) {
$octal = $octal[0];
}
while ($octal && eval('return ' . $octal . ';') > 255) {
$octal = \substr($octal, 0, -1);
}
if ($octal === '0') {
$octal = null;
}
++$this->tokPos;
if ($octal) {
if ($this->strict) {
$this->raise($this->tokPos - 2, 'Octal literal in strict mode');
}
$str .= \chr(eval('return ' . $octal . ';'));
$this->tokPos += \strlen($octal) - 1;
} elseif ($ch === 'x') {
$str .= $this->readHexChar(2);
} elseif ($ch === 'u') {
$str .= $this->readHexChar(4);
} elseif ($ch === 'U') {
$str .= $this->readHexChar(8);
} else {
switch ($ch) {
case 'n': $str .= "\x0a"; break;
case 'r': $str .= "\x0d"; break;
case 't': $str .= "\x09"; break;
case 'b': $str .= "\x08"; break;
case 'v': $str .= "\x0b"; break;
case 'f': $str .= "\x0c"; break;
case '0': $str .= "\x00"; break;
case "\r":
if ($this->input[$this->tokPos] === "\n") {
++$this->tokPos;
}
case "\n":
break;
default:
$str .= $ch;
}
}
} else {
if (\preg_match(self::NEWLINE, $ch)) {
$this->raise($this->tokStart, 'Unterminated string constant');
}
$str .= $ch;
++$this->tokPos;
}
}
}
protected function readHexChar($len) {
$n = $this->readInt(16, $len);
if ($n === null) {
$this->raise($this->tokStart, 'Bad character escape sequence');
}
return \chr($n);
}
protected function readWord1() {
$this->containsEsc = false;
$first = true;
$start = $this->tokPos;
$word = '';
while ($this->tokPos < $this->inputLen) {
$ch = $this->input[$this->tokPos];
if ((($ch >= 'a' && $ch <= 'z') || ($ch >= 'A' && $ch <= 'Z') ||
($ch >= '0' && $ch <= '9') || $ch === '$' || $ch === '_') ||
($ch >= "\xaa" && preg_match(self::NON_ASCII_IDENTIFIER, $ch))) {
if ($this->containsEsc) {
$word .= $ch;
}
++$this->tokPos;
} elseif ($ch === '\\') {
if (!$this->containsEsc) {
$word = \substr($this->input, $start, $this->tokPos - $start);
}
$this->containsEsc = true;
if ($this->input[$this->tokPos + 1] !== 'u') {
$this->raise($this->tokPos, 'Expecting Unicode escape sequence \\uXXXX');
}
++$this->tokPos;
$esc = $this->readHexChar(4);
if (!$esc) {
$this->raise($this->tokPos - 1, 'Invalid Unicode escape');
}
if (!((($esc >= 'a' && $esc <= 'z') || ($esc >= 'A' && $esc <= 'Z') ||
$esc === '$' || $esc === '_') || (!$first && $esc >= '0' && $esc <= '9') ||
($esc >= "\xaa" && preg_match($first ? self::NON_ASCII_IDENTIFIER_START : self::NON_ASCII_IDENTIFIER, $esc)))) {
$this->raise($this->tokPos, 'Invalid Unicode escape');
}
$word .= $esc;
} else {
break;
}
$first = false;
}
return $this->containsEsc ? $word : \substr($this->input, $start, $this->tokPos - $start);
}
public function readWord() {
$word = $this->readWord1();
$type = $this->_name;
if (!$this->containsEsc) {
if ($this->isKeyword($word)) {
$type = $this->keywordTypes[$word];
} elseif ($this->options['forbidReserved'] &&
($this->options['ecmaVersion'] === 3 ? $this->isReservedWord3($word) : $this->isReservedWord5($word)) ||
$this->strict && $this->isStrictReservedWord($word)) {
$this->raise($this->tokStart, 'The keyword "' . $word . '" is reserved');
}
}
return $this->finishToken($type, $word);
}
//
// PARSER
//
protected function next() {
$this->lastStart = $this->tokStart;
$this->lastEnd = $this->tokEnd;
$this->lastEndLoc = $this->tokEndLoc;
$this->readToken();
}
protected function setStrict($strict) {
$this->strict = $strict;
$this->tokPos = $this->lastEnd;
$this->skipSpace();
$this->readToken();
}
protected function nodeFor($type, $options = array()) {
if ($type !== null && class_exists($class = '\\JavaScript\\AST\\Node\\' . $type)) {
$result = call_user_func(array($class, 'get'), $options);
} else {
$result = \JavaScript\AST\Node::get($options);
$result->type = $type;
}
return $result;
}
protected function startNode($type = null) {
if ($type instanceof \JavaScript\AST\Node) {
$node = $type;
$node->start = $this->tokStart;
} else {
$node = $this->nodeFor($type, array('start' => $this->tokStart));
}
if ($this->options['trackComments'] && $this->tokCommentsBefore) {
$node->commentsBefore = $this->tokCommentsBefore;
$this->tokCommentsBefore = null;
}
if ($this->options['locations']) {
$node->loc = array('start' => $this->tokStartLoc, 'end' => null, 'source' => $this->sourceFile);
}
if ($this->options['ranges']) {
$node->range = array($this->tokStart, 0);
}
return $node;
}
protected function startNodeFrom(\JavaScript\AST\Node $other, $type = null) {
if ($type instanceof \JavaScript\AST\Node) {
$node = $type;
$node->start = $other->start;
} else {
$node = $this->nodeFor($type, array('start' => $other->start));
}
if ($other->commentsBefore) {
$node->commentsBefore = $other->tokCommentsBefore;
$other->tokCommentsBefore = null;
}
if ($this->options['locations']) {
$node->loc = array('start' => $other->loc['start'], 'end' => null, 'source' => $other->loc['source']);
}
if ($this->options['ranges']) {
$node->range = array($other->range[0], 0);
}
return $node;
}
protected function finishNode($node, $type = null) {
if ($type !== null) {
if (class_exists($class = '\\JavaScript\\AST\\Node\\' . $type)) {
$node = call_user_func(array($class, 'get'), \get_object_vars($node));
}
$node->type = $type;
}
$node->end = $this->lastEnd;
if ($this->options['trackComments']) {
if ($this->tokCommentsAfter) {
$node->commentsAfter = $this->tokCommentsAfter;
$this->tokCommentsAfter = null;
} elseif ($this->lastFinishedNode && $this->lastFinishedNode->end === $this->lastEnd) {
$node->commentsAfter = $this->lastFinishedNode->commentsAfter;
$this->lastFinishedNode->commentsAfter = null;
}
$this->lastFinishedNode = $node;
}
if ($this->options['locations']) {
$node->loc['end'] = $this->lastEndLoc;
}
if ($this->options['ranges']) {
$node->range[1] = $this->lastEnd;
}
return $node;
}
protected function isUseStrict($stmt) {
return $this->options['ecmaVersion'] >= 5 && $stmt->type === 'ExpressionStatement' &&
$stmt->expression->type === 'Literal' && $stmt->expression->value === 'use strict';
}
protected function eat($type) {
if ($this->tokType === $type) {
$this->next();
return true;
}
}
protected function canInsertSemicolon() {
return $this->tokType === $this->_eof || $this->tokType === $this->_braceR ||
!$this->options['strictSemicolons'] &&
preg_match(self::NEWLINE, substr($this->input, $this->lastEnd, $this->tokStart - $this->lastEnd));
}
protected function semicolon() {
if (!$this->eat($this->_semi) && !$this->canInsertSemicolon()) {
$this->unexpected();
}
}
protected function expect($type) {
if ($this->tokType === $type) {
$this->next();
} else {
$this->unexpected();
}
}
protected function unexpected() {
$this->raise($this->tokStart, 'Unexpected token "' . $this->tokVal . '"');
}
protected function checkLVal($expr) {
if ($expr->type !== 'Identifier' && $expr->type !== 'MemberExpression') {
$this->raise($expr->start, 'Assigning to rvalue');
}
if ($this->strict && $expr->type === 'Identifier' && $this->isStrictBadIdWord($expr->name)) {
$this->raise($expr->start, 'Assigning to ' . $expr->name . ' in strict mode');
}
}
//
// Statement parsing
//
protected function parseTopLevel($program) {
$this->initTokenState();
$this->lastStart = $this->lastEnd = $this->tokPos;
if ($this->options['locations']) {
$this->lastEndLoc = $this->curLineLoc();
}
$this->inFunction = $this->strict = null;
$this->labels = array();
$this->readToken();
$node = $program ?: $this->startNode(new \JavaScript\AST\Node\Program);
$first = true;
while ($this->tokType !== $this->_eof) {
$node->body[] = $stmt = $this->parseStatement();
if ($first && $this->isUseStrict($stmt)) {
$this->setStrict(true);
}
$first = false;
}
return $this->finishNode($node);
}
protected function parseStatement() {
if ($this->tokType === $this->_slash) {
$this->readToken(true);
}
$starttype = $this->tokType;
switch ($starttype) {
case $this->_break: case $this->_continue:
$this->next();
$isBreak = $starttype === $this->_break;
$node = $this->startNode($isBreak ? 'BreakStatement' : 'ContinueStatement');
if ($this->eat($this->_semi) || $this->canInsertSemicolon()) {
$node->label = null;
} elseif ($this->tokType !== $this->_name) {
$this->unexpected();
} else {
$node->label = $this->parseIdent();
$this->semicolon();
}
for ($i = 0; $i < count($this->labels); ++$i) {
$lab = $this->labels[$i];
if ($node->label === null || $lab->name === $node->label->name) {
if ($lab->kind !== null && ($isBreak || $lab->kind === 'loop')) {
break;
}
if ($node->label && $isBreak) {
break;
}
}
}
if ($i === count($this->labels)) {
$this->raise($node->start, 'Unsyntactic ' . $starttype['keyword']);
}
return $this->finishNode($node);
case $this->_debugger:
$this->next();
return $this->finishNode($this->startNode('DebuggerStatement'));
case $this->_do:
$this->next();
$this->labels[] = $this->loopLabel;
$node = $this->startNode('DoWhileStatement');
$node->body = $this->parseStatement();
array_pop($this->labels);
$this->expect($this->_while);
$node->test = $this->parseParenExpression();
$this->semicolon();
return $this->finishNode($node);
case $this->_for:
$this->next();
$this->labels[] = $this->loopLabel;
$this->expect($this->_parenL);
if ($this->tokType === $this->_semi) {
return $this->parseFor($this->startNode('ForStatement'), null);
}
if ($this->tokType === $this->_var) {
$init = $this->startNode('VariableDeclaration');
$this->next();
$this->parseVar($init, true);
if (count($init->declarations) === 1 && $this->eat($this->_in)) {
return $this->parseForIn($this->startNode('ForInStatement'), $init);
}
return $this->parseFor($this->startNode('ForStatement'), $init);
}
$init = $this->parseExpression(false, true);
if ($this->eat($this->_in)) {
$this->checkLVal($init);
return $this->parseForIn($this->startNode('ForInStatement'), $init);
}
return $this->parseFor($this->startNode('ForStatement'), $init);
case $this->_function:
$this->next();
return $this->parseFunction($this->startNode(new \JavaScript\AST\Node\FunctionDeclaration), true);
case $this->_if:
$this->next();
$node = $this->startNode('IfStatement');
$node->test = $this->parseParenExpression();
$node->consequent = $this->parseStatement();
$node->alternate = $this->eat($this->_else) ? $this->parseStatement() : null;
return $this->finishNode($node);
case $this->_return:
if (!$this->inFunction) {
$this->raise('"return" outside of function');
}
$this->next();
$node = $this->startNode('ReturnStatement');
if ($this->eat($this->_semi) || $this->canInsertSemicolon()) {
$node->argument = null;
} else {
$node->argument = $this->parseExpression();
$this->semicolon();
}
return $this->finishNode($node);
case $this->_switch:
$this->next();
$node = $this->startNode('SwitchStatement');
$node->discriminant = $this->parseParenExpression();
$node->cases = array();
$this->expect($this->_braceL);
$this->labels[] = $this->switchLabel;
for ($cur = null, $sawDefault = false; $this->tokType !== $this->_braceR;) {
if ($this->tokType === $this->_case || $this->tokType === $this->_default) {
$isCase = $this->tokType === $this->_case;
if ($cur) {
$this->finishNode($cur);
}
$node->cases[] = $cur = $this->startNode('SwitchCase');
$cur->consequent = array();
$this->next();
if ($isCase) {
$cur->test = $this->parseExpression();
} else {
if ($sawDefault) {
$this->raise($this->lastStart, 'Multiple default clauses');
}
$sawDefault = true;
$cur->test = null;
}
$this->expect($this->_colon);
} else {
if (!$cur) {
$this->unexpected();
}
$cur->consequent[] = $this->parseStatement();
}
}
if ($cur) {
$this->finishNode($cur);
}
$this->next();
array_pop($this->labels);
return $this->finishNode($node);
case $this->_throw:
$this->next();
$node = $this->startNode('ThrowStatement');
if ($this->canInsertSemicolon()) {
$this->raise($this->lastEndLoc, 'Invalid newline after throw');
}
$node->argument = $this->parseExpression();
return $this->finishNode($node);
case $this->_try:
$this->next();
$node = $this->startNode('TryStatement');
$node->block = $this->parseBlock();
$node->handlers = array();
while ($this->tokType === $this->_catch) {
$clause = $this->startNode('CatchClause');
$this->next();
$this->expect($this->_parenL);
$clause->param = $this->parseIdent();
if ($this->strict && $this->isStrictBadWord($clause->param->name)) {
$this->raise($clause->param->start, 'Binding ' . $clause->param->name . ' in strict mode');
}
$this->expect($this->_parenR);
$clause->guard = null;
$clause->body = $this->parseBlock();
$node->handlers[] = $this->finishNode($clause);
}
$node->finalizer = $this->eat($this->_finally) ? $this->parseBlock() : null;
if (!$node->handlers && !$node->finalizer) {
$this->raise($node->start, 'Missing catch or finally clause');
}
return $this->finishNode($node);
case $this->_var:
$this->next();
$node = $this->parseVar($this->startNode('VariableDeclaration'));
$this->semicolon();
return $node;
case $this->_while:
$this->next();
$node = $this->startNode('WhileStatement');
$node->test = $this->parseParenExpression();
$this->labels[] = $this->loopLabel;
$node->body = $this->parseStatement();
array_pop($this->labels);
return $this->finishNode($node);
case $this->_with:
if ($this->strict) {
$this->raise($this->tokStart, '"with" in strict mode');
}
$this->next();
$node = $this->startNode('WithStatement');
$node->object = $this->parseParenExpression();
$node->body = $this->parseStatement();
return $this->finishNode($node);
case $this->_braceL:
return $this->parseBlock();
case $this->_semi:
$this->next();
return $this->finishNode($this->startNode('EmptyStatement'));
default:
$maybeName = $this->tokVal;
$expr = $this->parseExpression();
if ($starttype === $this->_name && $expr->type === 'Identifier' && $this->eat($this->_colon)) {
for ($i = 0; $i < count($this->labels); ++$i) {
if ($this->labels[$i]->name === $maybeName) {
$this->raise($expr->start, 'Label "' . $maybeName . '" is already declared');
}
}
$kind = $this->tokType['isLoop'] ? 'loop' : ($this->tokType === $this->_switch ? 'switch' : null);
$this->labels[] = \JavaScript\AST\Node::get(array('name' => $maybeName, 'kind' => $kind));
$node = $this->startNode('LabeledStatement');
$node->body = $this->parseStatement();
$node->label = $expr;
return $this->finishNode($node);
}
$node = $this->startNode('ExpressionStatement');
$node->expression = $expr;
$this->semicolon();
return $this->finishNode($node);
}
}
protected function parseParenExpression() {
$this->expect($this->_parenL);
$val = $this->parseExpression();
$this->expect($this->_parenR);
return $val;
}
protected function parseVar(\JavaScript\AST\Node $node, $noIn = false) {
$node->declarations = array();
$node->kind = 'var';
for (;;) {
$decl = $this->startNode(new \JavaScript\AST\Node\VariableDeclarator);
$decl->id = $this->parseIdent();
if ($this->strict) {
}
if ($this->eat($this->_eq)) {
$node->decl = $this->parseExpression(true, $noIn);
} else {
$node->decl = null;
}
$node->declarations[] = $this->finishNode($decl);
if (!$this->eat($this->_comma)) {
break;
}
}
return $this->finishNode($node);
}
protected function parseBlock($allowStrict = false) {
$node = $this->startNode();
$first = true;
$strict = false;
$oldStrict = null;
$node->body = array();
$this->expect($this->_braceL);
while (!$this->eat($this->_braceR)) {
$stmt = $this->parseStatement();
$node->body[] = $stmt;
if ($first && $allowStrict && $this->isUseStrict($stmt)) {
$oldStrict = $strict;
$this->setStrict($strict = true);
}
$first = false;
}
if ($strict && !$oldStrict) {
$this->setStrict(false);
}
return $this->finishNode($node, 'BlockStatement');
}
protected function parseFor(\JavaScript\AST\Node $node, \JavaScript\AST\Node $init = null) {
$node->init = $init;
$this->expect($this->_semi);
$node->test = $this->tokType === $this->_semi ? null : $this->parseExpression();
$this->expect($this->_semi);
$node->test = $this->tokType === $this->_parenR ? null : $this->parseExpression();
$this->expect($this->_parenR);
$node->body = $this->parseStatement();
array_pop($this->labels);
return $this->finishNode($node);
}
protected function parseForIn(\JavaScript\AST\Node $node, \JavaScript\AST\Node $init) {
$node->left = $init;
$node->right = $this->parseExpression();
$this->expect($this->_parenR);
$node->body = $this->parseStatement();
array_pop($this->labels);
return $this->finishNode($node);
}
protected function parseExpression($noComma = false, $noIn = false) {
$expr = $this->parseMaybeAssign($noIn);
if (!$noComma && $this->tokType === $this->_comma) {
$node = $this->startNodeFrom($expr, new \JavaScript\AST\Node\SequenceExpression());
$node->expressions = array($expr);
while ($this->eat($this->_comma)) {
$node->expressions[] = $this->parseMaybeAssign($noIn);
}
return $this->finishNode($node);
}
return $expr;
}
public function parseMaybeAssign($noIn = false) {
$left = $this->parseMaybeConditional($noIn);
if (isset($this->tokType['isAssign'])) {
$node = $this->startNodeFrom($left, 'AssignmentExpression');
$node->operator = $this->tokVal;
$node->left = $left;
$this->next();
$node->right = $this->parseMaybeAssign($noIn);
$this->checkLVal($left);
return $this->finishNode($node);
}
return $left;
}
public function parseMaybeConditional($noIn) {
$expr = $this->parseExprOps($noIn);
if ($this->eat($this->_question)) {
$node = $this->startNodeFrom($expr, 'ConditionalExpression');
$node->test = $expr;
$node->consequent = $this->parseExpression(true);
$this->expect($this->_colon);
$node->alternate = $this->parseExpression(true, $noIn);
return $this->finishNode($node);
}
return $expr;
}
public function parseExprOps($noIn) {
return $this->parseExprOp($this->parseMaybeUnary($noIn), -1, $noIn);
}
protected function parseExprOp(\JavaScript\AST\Node $left, $minPrec, $noIn) {
$prec = isset($this->tokType['binop']) ? $this->tokType['binop'] : null;
if ($prec !== null && (!$noIn || $this->tokType !== $this->_in)) {
if ($prec > $minPrec) {
$node = $this->startNodeFrom(
$left,
$this->tokVal === '&&' || $this->tokVal === '||' ? 'LogicalExpression' : 'BinaryExpression'
);
$node->left = $left;
$node->operator = $this->tokVal;
$this->next();
$node->right = $this->parseExprOp($this->parseMaybeUnary($noIn), $prec, $noIn);
$node1 = $this->finishNode($node);
return $this->parseExprOp($node1, $minPrec, $noIn);
}
}
return $left;
}
protected function parseMaybeUnary($noIn) {
if (isset($this->tokType['prefix'])) {
$update = isset($this->tokType['isUpdate']);
$node = $this->startNode($update ? 'UpdateExpression' : 'UnaryExpression');
$node->operator = $this->tokVal;
$node->prefix = true;
$this->next();
$node->argument = $this->parseMaybeUnary($noIn);
if ($update) {
$this->checkLVal($node->argument);
} elseif ($this->strict && $node->operator === 'delete' &&
$node->argument->type === 'Identifier') {
$this->raise($node->start, 'Deleting local variable in strict mode');
}
return $this->finishNode($node);
}
$expr = $this->parseExprSubscripts();
while (isset($this->tokType['postfix']) && !$this->canInsertSemicolon()) {
$node = $this->startNodeFrom($expr, 'UpdateExpression');
$node->operator = $this->tokVal;
$node->prefix = false;
$node->argument = $expr;
$this->checkLVal($expr);
$this->next();
$expr = $this->finishNode($node);
}
return $expr;
}
protected function parseExprSubscripts() {
return $this->parseSubscripts($this->parseExprAtom());
}
protected function parseSubscripts(\JavaScript\AST\Node $base, $noCalls = false) {
if ($this->eat($this->_dot)) {
$node = $this->startNodeFrom($base, 'MemberExpression');
$node->object = $base;
$node->property = $this->parseIdent(true);
$node->computed = false;
return $this->parseSubscripts($this->finishNode($node), $noCalls);
}
if ($this->eat($this->_bracketL)) {
$node = $this->startNodeFrom($base, 'MemberExpression');
$node->object = $base;
$node->property = $this->parseExpression();
$node->computed = true;
$this->expect($this->_bracketR);
return $this->parseSubscripts($this->finishNode($node), $noCalls);
}
if (!$noCalls && $this->eat($this->_parenL)) {
$node = $this->startNodeFrom($base, 'CallExpression');
$node->callee = $base;
$node->arguments = $this->parseExprList($this->_parenR, false);
return $this->parseSubscripts($this->finishNode($node), $noCalls);
}
return $base;
}
protected function parseExprAtom() {
switch ($this->tokType) {
case $this->_this:
$node = $this->startNode(new \JavaScript\AST\Node\ThisExpression);
$this->next();
return $this->finishNode($node);
case $this->_name:
return $this->parseIdent();
case $this->_num: case $this->_string: case $this->_regexp:
$node = $this->startNode(new \JavaScript\AST\Node\Literal);
$node->value = $this->tokVal;
$this->next();
return $this->finishNode($node);
case $this->_null: case $this->_true: case $this->_false:
$node = $this->startNode(new \JavaScript\AST\Node\Literal);
$node->value = $this->tokType['atomValue'];
$this->next();
return $this->finishNode($node);
case $this->_parenL:
$this->next();
$val = $this->parseExpression();
$this->expect($this->_parenR);
return $val;
case $this->_bracketL:
$node = $this->startNode(new \JavaScript\AST\Node\ArrayExpression);
$this->next();
$node->elements = $this->parseExprList($this->_bracketR, true, true);
return $this->finishNode($node);
case $this->_braceL:
return $this->parseObj();
case $this->_function:
$node = $this->startNode(new \JavaScript\AST\Node\FunctionExpression);
$this->next();
return $this->parseFunction($node, false);
case $this->_new:
return $this->parseNew();
default:
$this->unexpected();
}
}
protected function parseNew() {
$node = $this->startNode(new \JavaScript\AST\Node\NewExpression);
$this->next();
$node->callee = $this->parseSubscripts($this->parseExprAtom(false), true);
if ($this->eat($this->_parenL)) {
$node->arguments = $this->parseExprList($this->_parenR, false);
} else {
$node->arguments = array();
}
return $this->finishNode($node);
}
protected function parseObj() {
$node = $this->startNode(new \JavaScript\AST\Node\ObjectExpression);
$first = true;
$sawGetSet = false;
$node->properties = array();
$this->next();
while (!$this->eat($this->_braceR)) {
if (!$first) {
$this->expect($this->_comma);
if ($this->options['allowTrailingCommas'] && $this->eat($this->_braceR)) {
break;
}
} else {
$first = false;
}
$prop = \JavaScript\AST\Node::get(array('key' => $this->parsePropertyName()));
$isGetSet = false;
if ($this->eat($this->_colon)) {
$prop->value = $this->parseExpression(true);
$prop->kind = 'init';
} elseif ($this->options['ecmaVersion'] >= 5 && $prop->key->type === 'Identifier' &&
($prop->key->name === 'get' || $prop->key->name === 'set')) {
$isGetSet = $sawSetGet = true;
$prop->kind = $prop->key->name;
$prop->key = $this->parsePropertyName();
//if (!($this->tokType === $this->_parenL)) {
// $this->unexpected();
//}
$prop->value = $this->parseFunction($this->startNode(new \JavaScript\AST\Node\FunctionExpression), false);
} else {
$this->unexpected();
}
if ($prop->key->type === 'Identifier' && ($this->strict || $sawGetSet)) {
foreach ($node->properties as $other) {
if ($other->key->name === $prop->key->name) {
$conflict = $prop->kind === $other->kind || $isGetSet && $other->kind === 'init' ||
$prop->kind === 'init' && ($other->kind === 'init' || $other->kind === 'set');
if ($conflict && !$this->strict && $prop->kind === 'init' && $other->kind === 'init') {
$conflict = false;
}
if ($conflict) {
$this->raise($prop->key->start, 'Redefinition of property');
}
}
}
}
$node->properties[] = $prop;
}
return $this->finishNode($node);
}
protected function parsePropertyName() {
if ($this->tokType === $this->_num || $this->tokType === $this->_string) {
return $this->parseExprAtom();
}
return $this->parseIdent(true);
}
protected function parseFunction(\JavaScript\AST\Node $node, $isStatement = false) {
if ($this->tokType === $this->_name) {
$node->id = $this->parseIdent();
} elseif ($isStatement) {
$this->unexpected();
} else {
$node->id = null;
}
$node->params = array();
$first = true;
$this->expect($this->_parenL);
while (!$this->eat($this->_parenR)) {
if (!$first) {
$this->expect($this->_comma);
} else {
$first = false;
}
$node->params[] = $this->parseIdent();
}
$oldInFunc = $this->inFunction;
$oldLabels = $this->labels;
$this->inFunction = true;
$this->labels = array();
$node->body = $this->parseBlock(true);
$this->inFunction = $oldInFunc;
$this->labels = $oldLabels;
if ($this->strict || $node->body->body && $this->isUseStrict($node->body->body[0])) {
for ($i = $node->id ? -1 : 0; $i < count($node->params); ++$i) {
$id = $i < 0 ? $node->id : $node->params[$i];
if ($this->isStrictReservedWord($id->name) || $this->isStrictBadWord($id->name)) {
$this->raise($id->start, 'Defining "' . $id->name . '" in strict mode');
}
if ($i >= 0) {
for ($j = 0; $j < $i; ++$j) {
if ($id->name === $node->params[$j]->name) {
$this->raise($id->start, 'Argument name clash in strict mode');
}
}
}
}
}
return $this->finishNode($node);
}
protected function parseExprList($close, $allowTrailingComma = false, $allowEmpty = false) {
$elts = array();
$first = true;
while (!$this->eat($close)) {
if (!$first) {
$this->expect($this->_comma);
if ($allowTrailingComma && $this->options['allowTrailingCommas'] && $this->eat($close)) {
break;
}
} else {
$first = false;
}
if ($allowEmpty && $this->tokType === $this->_comma) {
$elts[] = null;
} else {
$elts[] = $this->parseExpression(true);
}
}
return $elts;
}
protected function parseIdent($liberal = false) {
$node = $this->startNode(new \JavaScript\AST\Node\Identifier);
if ($this->tokType === $this->_name) {
$node->name = $this->tokVal;
} elseif ($liberal && isset($this->tokType['keyword'])) {
$node->name = $this->tokType['keyword'];
} else {
$this->unexpected();
}
$this->next();
return $this->finishNode($node);
}
}
namespace JavaScript\AST;
class Node implements \JavaScript\AST\Tree\Node {
public $type;
public $start = 0;
public $end;
public static function get(array $def = array()) {
$node = new static;
foreach($def as $k => $v) {
$node->$k = $v;
}
return $node;
}
public function __get($key) {
isset($key);
return null;
}
}
namespace JavaScript\AST\Tree;
interface Node { }
interface Pattern extends Node { }
interface Program extends Node { }
interface Lambda extends Node { }
interface Statement extends Node { }
interface EmptyStatement extends Statement { }
interface BlockStatement extends Statement { }
interface ExpressionStatement extends Statement { }
interface IfStatement extends Statement { }
interface LabeledStatement extends Statement { }
interface BreakStatement extends Statement { }
interface ContinueStatement extends Statement { }
interface WithStatement extends Statement { }
interface SwitchStatement extends Statement { }
interface ReturnStatement extends Statement { }
interface ThrowStatement extends Statement { }
interface TryStatement extends Statement { }
interface WhileStatement extends Statement { }
interface DoWhileStatement extends Statement { }
interface ForStatement extends Statement { }
interface ForInStatement extends Statement { }
interface DebuggerStatement extends Statement { }
interface Declaration extends Statement { }
interface FunctionDeclaration extends Lambda, Declaration { }
interface VariableDeclaration extends Declaration { }
interface VariableDeclarator extends Node { }
interface Expression extends Node, Pattern { }
interface ThisExpression extends Expression { }
interface ArrayExpression extends Expression { }
interface ObjectExpression extends Expression { }
interface FunctionExpression extends Lambda, Expression { }
interface SequenceExpression extends Expression { }
interface UnaryExpression extends Expression { }
interface BinaryExpression extends Expression { }
interface AssignmentExpression extends Expression { }
interface UpdateExpression extends Expression { }
interface LogicalExpression extends Expression { }
interface ConditionalExpression extends Expression { }
interface NewExpression extends Expression { }
interface CallExpression extends Expression { }
interface MemberExpression extends Expression { }
interface ObjectPattern extends Pattern { }
interface ArrayPattern extends Pattern { }
interface SwitchCase extends Node { }
interface CatchClause extends Node { }
interface Identifier extends Node, Expression { }
interface Literal extends Node, Expression { }
namespace JavaScript\AST\Node;
class Program extends \JavaScript\AST\Node implements \JavaScript\AST\Tree\Program {
public $type = 'Program';
public $body = array();
}
class Identifier extends \JavaScript\AST\Node implements \JavaScript\AST\Tree\Identifier {
public $type = 'Identifier';
public $name;
}
class Expression extends \JavaScript\AST\Node implements \JavaScript\AST\Tree\Expression {
}
class ThisExpression extends \JavaScript\AST\Node\Expression implements \JavaScript\AST\Tree\ThisExpression {
public $type = 'ThisExpression';
}
class Literal extends \JavaScript\AST\Node\Expression implements \JavaScript\AST\Tree\Literal {
public $type = 'Literal';
public $value;
}
class NewExpression extends \JavaScript\AST\Node\Expression implements \JavaScript\AST\Tree\NewExpression {
public $type = 'NewExpression';
public $callee;
public $arguments = array();
}
class ObjectExpression extends \JavaScript\AST\Node\Expression implements \JavaScript\AST\Tree\ObjectExpression {
public $type = 'ObjectExpression';
public $properties = array();
}
class ArrayExpression extends \JavaScript\AST\Node\Expression implements \JavaScript\AST\Tree\ArrayExpression {
public $type = 'ArrayExpression';
public $elements = array();
}
class SequenceExpression extends \JavaScript\AST\Node\Expression implements \JavaScript\AST\Tree\SequenceExpression {
public $type = 'SequenceExpression';
public $expressions = array();
}
class VariableDeclarator extends \JavaScript\AST\Node implements \JavaScript\AST\Tree\VariableDeclarator {
public $type = 'VariableDeclarator';
public $id;
public $init;
}
class FunctionExpression extends \JavaScript\AST\Node\Expression implements \JavaScript\AST\Tree\FunctionExpression {
public $type = 'FunctionExpression';
public $id;
public $params = array();
public $body;
}
class Statement extends \JavaScript\AST\Node implements \JavaScript\AST\Tree\Statement {
}
class FunctionDeclaration extends \JavaScript\AST\Node\Statement implements \JavaScript\AST\Tree\FunctionDeclaration {
public $type = 'FunctionExpression';
public $id;
public $params = array();
public $body;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment