PageRenderTime 37ms CodeModel.GetById 13ms RepoModel.GetById 1ms app.codeStats 0ms

/grammar/rebuildParser.php

https://bitbucket.org/nilopc_repo/sf2_backup-php-parser
PHP | 212 lines | 143 code | 53 blank | 16 comment | 22 complexity | 04ad5cca5d86927445cf8eaa53a8162e MD5 | raw file
Possible License(s): BSD-3-Clause
  1. <?php
  2. const GRAMMAR_FILE = './zend_language_parser.phpy';
  3. const TMP_FILE = './tmp_parser.phpy';
  4. const RESULT_FILE = './tmp_parser.php';
  5. ///////////////////////////////
  6. /// Utility regex constants ///
  7. ///////////////////////////////
  8. const LIB = '(?(DEFINE)
  9. (?<singleQuotedString>\'[^\\\\\']*+(?:\\\\.[^\\\\\']*+)*+\')
  10. (?<doubleQuotedString>"[^\\\\"]*+(?:\\\\.[^\\\\"]*+)*+")
  11. (?<string>(?&singleQuotedString)|(?&doubleQuotedString))
  12. (?<comment>/\*[^*]*+(?:\*(?!/)[^*]*+)*+\*/)
  13. (?<code>\{[^\'"/{}]*+(?:(?:(?&string)|(?&comment)|(?&code)|/)[^\'"/{}]*+)*+})
  14. )';
  15. const PARAMS = '\[(?<params>[^[\]]*+(?:\[(?&params)\][^[\]]*+)*+)\]';
  16. const ARGS = '\((?<args>[^()]*+(?:\((?&args)\)[^()]*+)*+)\)';
  17. ///////////////////
  18. /// Main script ///
  19. ///////////////////
  20. echo '<pre>';
  21. echo 'Building temporary preproprocessed grammar file.', "\n";
  22. $grammarCode = file_get_contents(GRAMMAR_FILE);
  23. $grammarCode = resolveConstants($grammarCode);
  24. $grammarCode = resolveNodes($grammarCode);
  25. $grammarCode = resolveMacros($grammarCode);
  26. $grammarCode = resolveArrays($grammarCode);
  27. file_put_contents(TMP_FILE, $grammarCode);
  28. echo 'Building parser. Output: "',
  29. trim(shell_exec('kmyacc -l -m kmyacc.php.parser -p PHPParser_Parser ' . TMP_FILE . ' 2>&1')),
  30. '"', "\n";
  31. rename(RESULT_FILE, '../lib/PHPParser/Parser.php');
  32. if (isset($_GET['debug'])) {
  33. echo 'Building debug parser. Output: "',
  34. trim(shell_exec('kmyacc -t -v -l -m kmyacc.php.parser -p PHPParser_Parser ' . TMP_FILE . ' 2>&1')),
  35. '"', "\n";
  36. if (!is_dir('../lib/PHPParser/Parser')) {
  37. mkdir('../lib/PHPParser/Parser');
  38. }
  39. rename(RESULT_FILE, '../lib/PHPParser/Parser/Debug.php');
  40. }
  41. unlink(TMP_FILE);
  42. echo 'The following temporary preproprocessed grammar file was used:', "\n", $grammarCode;
  43. echo '</pre>';
  44. ///////////////////////////////
  45. /// Preprocessing functions ///
  46. ///////////////////////////////
  47. function resolveConstants($code) {
  48. return preg_replace('~[A-Z][a-zA-Z_]++::~', 'PHPParser_Node_$0', $code);
  49. }
  50. function resolveNodes($code) {
  51. return preg_replace_callback(
  52. '~(?<name>[A-Z][a-zA-Z_]++)\s*' . PARAMS . '~',
  53. function($matches) {
  54. // recurse
  55. $matches['params'] = resolveNodes($matches['params']);
  56. $params = magicSplit(
  57. '(?:' . PARAMS . '|' . ARGS . ')(*SKIP)(*FAIL)|,',
  58. $matches['params']
  59. );
  60. $paramCode = '';
  61. foreach ($params as $param) {
  62. $paramCode .= $param . ', ';
  63. }
  64. return 'new PHPParser_Node_' . $matches['name'] . '(' . $paramCode . '$attributes)';
  65. },
  66. $code
  67. );
  68. }
  69. function resolveMacros($code) {
  70. return preg_replace_callback(
  71. '~\b(?<!::|->)(?!array\()(?<name>[a-z][A-Za-z]++)' . ARGS . '~',
  72. function($matches) {
  73. // recurse
  74. $matches['args'] = resolveMacros($matches['args']);
  75. $name = $matches['name'];
  76. $args = magicSplit(
  77. '(?:' . PARAMS . '|' . ARGS . ')(*SKIP)(*FAIL)|,',
  78. $matches['args']
  79. );
  80. if ('error' == $name) {
  81. assertArgs(1, $args, $name);
  82. return 'throw new PHPParser_Error(' . $args[0] . ')';
  83. }
  84. if ('init' == $name) {
  85. return '$$ = array(' . implode(', ', $args) . ')';
  86. }
  87. if ('push' == $name) {
  88. assertArgs(2, $args, $name);
  89. return $args[0] . '[] = ' . $args[1] . '; $$ = ' . $args[0];
  90. }
  91. if ('pushNormalizing' == $name) {
  92. assertArgs(2, $args, $name);
  93. return 'if (is_array(' . $args[1] . ')) { $$ = array_merge(' . $args[0] . ', ' . $args[1] . '); } else { ' . $args[0] . '[] = ' . $args[1] . '; $$ = ' . $args[0] . '; }';
  94. }
  95. if ('toArray' == $name) {
  96. assertArgs(1, $args, $name);
  97. return 'is_array(' . $args[0] . ') ? ' . $args[0] . ' : array(' . $args[0] . ')';
  98. }
  99. if ('parseVar' == $name) {
  100. assertArgs(1, $args, $name);
  101. return 'substr(' . $args[0] . ', 1)';
  102. }
  103. if ('parseEncapsed' == $name) {
  104. assertArgs(2, $args, $name);
  105. return 'foreach (' . $args[0] . ' as &$s) { if (is_string($s)) { $s = PHPParser_Node_Scalar_String::parseEscapeSequences($s, ' . $args[1] . '); } }';
  106. }
  107. if ('parseEncapsedDoc' == $name) {
  108. assertArgs(1, $args, $name);
  109. return 'foreach (' . $args[0] . ' as &$s) { if (is_string($s)) { $s = PHPParser_Node_Scalar_String::parseEscapeSequences($s, null); } } $s = preg_replace(\'~(\r\n|\n|\r)$~\', \'\', $s); if (\'\' === $s) array_pop(' . $args[0] . ');';
  110. }
  111. throw new Exception(sprintf('Unknown macro "%s"', $name));
  112. },
  113. $code
  114. );
  115. }
  116. function assertArgs($num, $args, $name) {
  117. if ($num != count($args)) {
  118. die('Wrong argument count for ' . $name . '().');
  119. }
  120. }
  121. function resolveArrays($code) {
  122. return preg_replace_callback(
  123. '~' . PARAMS . '~',
  124. function ($matches) {
  125. $elements = magicSplit(
  126. '(?:' . PARAMS . '|' . ARGS . ')(*SKIP)(*FAIL)|,',
  127. $matches['params']
  128. );
  129. // don't convert [] to array, it might have different meaning
  130. if (empty($elements)) {
  131. return $matches[0];
  132. }
  133. $elementCodes = array();
  134. foreach ($elements as $element) {
  135. // convert only arrays where all elements have keys
  136. if (false === strpos($element, ':')) {
  137. return $matches[0];
  138. }
  139. list($key, $value) = explode(':', $element, 2);
  140. $elementCodes[] = "'" . $key . "' =>" . $value;
  141. }
  142. return 'array(' . implode(', ', $elementCodes) . ')';
  143. },
  144. $code
  145. );
  146. }
  147. //////////////////////////////
  148. /// Regex helper functions ///
  149. //////////////////////////////
  150. function regex($regex) {
  151. return '~' . LIB . '(?:' . str_replace('~', '\~', $regex) . ')~';
  152. }
  153. function magicSplit($regex, $string) {
  154. $pieces = preg_split(regex('(?:(?&string)|(?&comment)|(?&code))(*SKIP)(*FAIL)|' . $regex), $string);
  155. foreach ($pieces as &$piece) {
  156. $piece = trim($piece);
  157. }
  158. return array_filter($pieces);
  159. }