PageRenderTime 1891ms CodeModel.GetById 38ms RepoModel.GetById 0ms app.codeStats 0ms

/vendor/nikic/php-parser/grammar/rebuildParser.php

https://gitlab.com/techniconline/kmc
PHP | 237 lines | 168 code | 52 blank | 17 comment | 23 complexity | 38e9f80544542aa84f535cf177438967 MD5 | raw file
  1. <?php
  2. $grammarFile = __DIR__ . '/zend_language_parser.phpy';
  3. $skeletonFile = __DIR__ . '/kmyacc.php.parser';
  4. $tmpGrammarFile = __DIR__ . '/tmp_parser.phpy';
  5. $tmpResultFile = __DIR__ . '/tmp_parser.php';
  6. $parserResultFile = __DIR__ . '/../lib/PhpParser/Parser.php';
  7. // check for kmyacc.exe binary in this directory, otherwise fall back to global name
  8. $kmyacc = __DIR__ . '/kmyacc.exe';
  9. if (!file_exists($kmyacc)) {
  10. $kmyacc = 'kmyacc';
  11. }
  12. $options = array_flip($argv);
  13. $optionDebug = isset($options['--debug']);
  14. $optionKeepTmpGrammar = isset($options['--keep-tmp-grammar']);
  15. ///////////////////////////////
  16. /// Utility regex constants ///
  17. ///////////////////////////////
  18. const LIB = '(?(DEFINE)
  19. (?<singleQuotedString>\'[^\\\\\']*+(?:\\\\.[^\\\\\']*+)*+\')
  20. (?<doubleQuotedString>"[^\\\\"]*+(?:\\\\.[^\\\\"]*+)*+")
  21. (?<string>(?&singleQuotedString)|(?&doubleQuotedString))
  22. (?<comment>/\*[^*]*+(?:\*(?!/)[^*]*+)*+\*/)
  23. (?<code>\{[^\'"/{}]*+(?:(?:(?&string)|(?&comment)|(?&code)|/)[^\'"/{}]*+)*+})
  24. )';
  25. const PARAMS = '\[(?<params>[^[\]]*+(?:\[(?&params)\][^[\]]*+)*+)\]';
  26. const ARGS = '\((?<args>[^()]*+(?:\((?&args)\)[^()]*+)*+)\)';
  27. ///////////////////
  28. /// Main script ///
  29. ///////////////////
  30. echo 'Building temporary preproprocessed grammar file.', "\n";
  31. $grammarCode = file_get_contents($grammarFile);
  32. $grammarCode = resolveNodes($grammarCode);
  33. $grammarCode = resolveMacros($grammarCode);
  34. $grammarCode = resolveArrays($grammarCode);
  35. $grammarCode = resolveStackAccess($grammarCode);
  36. file_put_contents($tmpGrammarFile, $grammarCode);
  37. $additionalArgs = $optionDebug ? '-t -v' : '';
  38. echo "Building parser.\n";
  39. $output = trim(shell_exec("$kmyacc $additionalArgs -l -m $skeletonFile $tmpGrammarFile 2>&1"));
  40. echo "Output: \"$output\"\n";
  41. $resultCode = file_get_contents($tmpResultFile);
  42. $resultCode = removeTrailingWhitespace($resultCode);
  43. ensureDirExists(dirname($parserResultFile));
  44. file_put_contents($parserResultFile, $resultCode);
  45. unlink($tmpResultFile);
  46. if (!$optionKeepTmpGrammar) {
  47. unlink($tmpGrammarFile);
  48. }
  49. ///////////////////////////////
  50. /// Preprocessing functions ///
  51. ///////////////////////////////
  52. function resolveNodes($code)
  53. {
  54. return preg_replace_callback(
  55. '~(?<name>[A-Z][a-zA-Z_\\\\]++)\s*' . PARAMS . '~',
  56. function ($matches) {
  57. // recurse
  58. $matches['params'] = resolveNodes($matches['params']);
  59. $params = magicSplit(
  60. '(?:' . PARAMS . '|' . ARGS . ')(*SKIP)(*FAIL)|,',
  61. $matches['params']
  62. );
  63. $paramCode = '';
  64. foreach ($params as $param) {
  65. $paramCode .= $param . ', ';
  66. }
  67. return 'new ' . $matches['name'] . '(' . $paramCode . 'attributes())';
  68. },
  69. $code
  70. );
  71. }
  72. function resolveMacros($code)
  73. {
  74. return preg_replace_callback(
  75. '~\b(?<!::|->)(?!array\()(?<name>[a-z][A-Za-z]++)' . ARGS . '~',
  76. function ($matches) {
  77. // recurse
  78. $matches['args'] = resolveMacros($matches['args']);
  79. $name = $matches['name'];
  80. $args = magicSplit(
  81. '(?:' . PARAMS . '|' . ARGS . ')(*SKIP)(*FAIL)|,',
  82. $matches['args']
  83. );
  84. if ('attributes' == $name) {
  85. assertArgs(0, $args, $name);
  86. return '$this->startAttributeStack[#1] + $this->endAttributes';
  87. }
  88. if ('init' == $name) {
  89. return '$$ = array(' . implode(', ', $args) . ')';
  90. }
  91. if ('push' == $name) {
  92. assertArgs(2, $args, $name);
  93. return $args[0] . '[] = ' . $args[1] . '; $$ = ' . $args[0];
  94. }
  95. if ('pushNormalizing' == $name) {
  96. assertArgs(2, $args, $name);
  97. return 'if (is_array(' . $args[1] . ')) { $$ = array_merge(' . $args[0] . ', ' . $args[1] . '); } else { ' . $args[0] . '[] = ' . $args[1] . '; $$ = ' . $args[0] . '; }';
  98. }
  99. if ('toArray' == $name) {
  100. assertArgs(1, $args, $name);
  101. return 'is_array(' . $args[0] . ') ? ' . $args[0] . ' : array(' . $args[0] . ')';
  102. }
  103. if ('parseVar' == $name) {
  104. assertArgs(1, $args, $name);
  105. return 'substr(' . $args[0] . ', 1)';
  106. }
  107. if ('parseEncapsed' == $name) {
  108. assertArgs(2, $args, $name);
  109. return 'foreach (' . $args[0] . ' as &$s) { if (is_string($s)) { $s = Node\Scalar\String_::parseEscapeSequences($s, ' . $args[1] . '); } }';
  110. }
  111. if ('parseEncapsedDoc' == $name) {
  112. assertArgs(1, $args, $name);
  113. return 'foreach (' . $args[0] . ' as &$s) { if (is_string($s)) { $s = Node\Scalar\String_::parseEscapeSequences($s, null); } } $s = preg_replace(\'~(\r\n|\n|\r)\z~\', \'\', $s); if (\'\' === $s) array_pop(' . $args[0] . ');';
  114. }
  115. return $matches[0];
  116. },
  117. $code
  118. );
  119. }
  120. function assertArgs($num, $args, $name)
  121. {
  122. if ($num != count($args)) {
  123. die('Wrong argument count for ' . $name . '().');
  124. }
  125. }
  126. function resolveArrays($code)
  127. {
  128. return preg_replace_callback(
  129. '~' . PARAMS . '~',
  130. function ($matches) {
  131. $elements = magicSplit(
  132. '(?:' . PARAMS . '|' . ARGS . ')(*SKIP)(*FAIL)|,',
  133. $matches['params']
  134. );
  135. // don't convert [] to array, it might have different meaning
  136. if (empty($elements)) {
  137. return $matches[0];
  138. }
  139. $elementCodes = array();
  140. foreach ($elements as $element) {
  141. // convert only arrays where all elements have keys
  142. if (false === strpos($element, ':')) {
  143. return $matches[0];
  144. }
  145. list($key, $value) = explode(':', $element, 2);
  146. $elementCodes[] = "'" . $key . "' =>" . $value;
  147. }
  148. return 'array(' . implode(', ', $elementCodes) . ')';
  149. },
  150. $code
  151. );
  152. }
  153. function resolveStackAccess($code)
  154. {
  155. $code = preg_replace('/\$\d+/', '$this->semStack[$0]', $code);
  156. $code = preg_replace('/#(\d+)/', '$$1', $code);
  157. return $code;
  158. }
  159. function removeTrailingWhitespace($code)
  160. {
  161. $lines = explode("\n", $code);
  162. $lines = array_map('rtrim', $lines);
  163. return implode("\n", $lines);
  164. }
  165. function ensureDirExists($dir)
  166. {
  167. if (!is_dir($dir)) {
  168. mkdir($dir, 0777, true);
  169. }
  170. }
  171. //////////////////////////////
  172. /// Regex helper functions ///
  173. //////////////////////////////
  174. function regex($regex)
  175. {
  176. return '~' . LIB . '(?:' . str_replace('~', '\~', $regex) . ')~';
  177. }
  178. function magicSplit($regex, $string)
  179. {
  180. $pieces = preg_split(regex('(?:(?&string)|(?&comment)|(?&code))(*SKIP)(*FAIL)|' . $regex), $string);
  181. foreach ($pieces as &$piece) {
  182. $piece = trim($piece);
  183. }
  184. return array_filter($pieces);
  185. }