function resolveMacros($code) { return preg_replace_callback('~\\b(?<!::|->)(?!array\\()(?<name>[a-z][A-Za-z]++)' . ARGS . '~', function ($matches) { // recurse $matches['args'] = resolveMacros($matches['args']); $name = $matches['name']; $args = magicSplit('(?:' . PARAMS . '|' . ARGS . ')(*SKIP)(*FAIL)|,', $matches['args']); if ('attributes' == $name) { assertArgs(0, $args, $name); return '$this->startAttributeStack[#1] + $this->endAttributes'; } if ('init' == $name) { return '$$ = array(' . implode(', ', $args) . ')'; } if ('push' == $name) { assertArgs(2, $args, $name); return $args[0] . '[] = ' . $args[1] . '; $$ = ' . $args[0]; } if ('pushNormalizing' == $name) { assertArgs(2, $args, $name); return 'if (is_array(' . $args[1] . ')) { $$ = array_merge(' . $args[0] . ', ' . $args[1] . '); }' . ' else { ' . $args[0] . '[] = ' . $args[1] . '; $$ = ' . $args[0] . '; }'; } if ('toArray' == $name) { assertArgs(1, $args, $name); return 'is_array(' . $args[0] . ') ? ' . $args[0] . ' : array(' . $args[0] . ')'; } if ('parseVar' == $name) { assertArgs(1, $args, $name); return 'substr(' . $args[0] . ', 1)'; } if ('parseEncapsed' == $name) { assertArgs(3, $args, $name); return 'foreach (' . $args[0] . ' as $s) { if ($s instanceof Node\\Scalar\\EncapsedStringPart) {' . ' $s->value = Node\\Scalar\\String_::parseEscapeSequences($s->value, ' . $args[1] . ', ' . $args[2] . '); } }'; } if ('parseEncapsedDoc' == $name) { assertArgs(2, $args, $name); return 'foreach (' . $args[0] . ' as $s) { if ($s instanceof Node\\Scalar\\EncapsedStringPart) {' . ' $s->value = Node\\Scalar\\String_::parseEscapeSequences($s->value, null, ' . $args[1] . '); } }' . ' $s->value = preg_replace(\'~(\\r\\n|\\n|\\r)\\z~\', \'\', $s->value);' . ' if (\'\' === $s->value) array_pop(' . $args[0] . ');'; } if ('makeNop' == $name) { assertArgs(2, $args, $name); return '$startAttributes = ' . $args[1] . ';' . ' if (isset($startAttributes[\'comments\']))' . ' { ' . $args[0] . ' = new Stmt\\Nop([\'comments\' => $startAttributes[\'comments\']]); }' . ' else { ' . $args[0] . ' = null; }'; } if ('strKind' == $name) { assertArgs(1, $args, $name); return '(' . $args[0] . '[0] === "\'" || (' . $args[0] . '[1] === "\'" && ' . '(' . $args[0] . '[0] === \'b\' || ' . $args[0] . '[0] === \'B\')) ' . '? Scalar\\String_::KIND_SINGLE_QUOTED : Scalar\\String_::KIND_DOUBLE_QUOTED)'; } if ('setDocStringAttrs' == $name) { assertArgs(2, $args, $name); return $args[0] . '[\'kind\'] = strpos(' . $args[1] . ', "\'") === false ' . '? Scalar\\String_::KIND_HEREDOC : Scalar\\String_::KIND_NOWDOC; ' . 'preg_match(\'/\\A[bB]?<<<[ \\t]*[\\\'"]?([a-zA-Z_\\x7f-\\xff][a-zA-Z0-9_\\x7f-\\xff]*)[\\\'"]?(?:\\r\\n|\\n|\\r)\\z/\', ' . $args[1] . ', $matches); ' . $args[0] . '[\'docLabel\'] = $matches[1];'; } if ('prependLeadingComments' == $name) { assertArgs(1, $args, $name); return '$attrs = $this->startAttributeStack[#1]; $stmts = ' . $args[0] . '; ' . 'if (!empty($attrs[\'comments\']) && isset($stmts[0])) {' . '$stmts[0]->setAttribute(\'comments\', ' . 'array_merge($attrs[\'comments\'], $stmts[0]->getAttribute(\'comments\', []))); }'; } return $matches[0]; }, $code); }
function resolveMacros($code) { return preg_replace_callback('~(?<name>init|push|pushNormalizing|toArray|parse(?:Var|Encapsed|LNumber|DNumber|String))' . ARGS . '~', function ($matches) { // recurse $matches['args'] = resolveMacros($matches['args']); $name = $matches['name']; $args = magicSplit('(?:' . PARAMS . '|' . ARGS . ')(*SKIP)(*FAIL)|,', $matches['args']); if ('init' == $name) { return '$$ = array(' . implode(', ', $args) . ')'; } if ('push' == $name) { assertArgs(2, $args, $name); return $args[0] . '[] = ' . $args[1] . '; $$ = ' . $args[0]; } if ('pushNormalizing' == $name) { assertArgs(2, $args, $name); return 'if (is_array(' . $args[1] . ')) { $$ = array_merge(' . $args[0] . ', ' . $args[1] . '); } else { ' . $args[0] . '[] = ' . $args[1] . '; $$ = ' . $args[0] . '; }'; } if ('toArray' == $name) { assertArgs(1, $args, $name); return 'is_array(' . $args[0] . ') ? ' . $args[0] . ' : array(' . $args[0] . ')'; } if ('parseVar' == $name) { assertArgs(1, $args, $name); return 'substr(' . $args[0] . ', 1)'; } if ('parseEncapsed' == $name) { assertArgs(1, $args, $name); return 'stripcslashes(' . $args[0] . ')'; } if ('parseLNumber' == $name) { assertArgs(1, $args, $name); return '(int) ' . $args[0]; } if ('parseDNumber' == $name) { assertArgs(1, $args, $name); return '(double) ' . $args[0]; } if ('parseString' == $name) { assertArgs(1, $args, $name); return 'str_replace(array(\'\\\\\\\'\', \'\\\\\\\\\'), array(\'\\\'\', \'\\\\\'), substr(' . $args[0] . ', 1, -1))'; } }, $code); }
function resolveMacros($code) { return preg_replace_callback('~\\b(?<!::|->)(?!array\\()(?<name>[a-z][A-Za-z]++)' . ARGS . '~', function ($matches) { // recurse $matches['args'] = resolveMacros($matches['args']); $name = $matches['name']; $args = magicSplit('(?:' . PARAMS . '|' . ARGS . ')(*SKIP)(*FAIL)|,', $matches['args']); if ('attributes' == $name) { assertArgs(0, $args, $name); return '$this->startAttributeStack[#1] + $this->endAttributes'; } if ('init' == $name) { return '$$ = array(' . implode(', ', $args) . ')'; } if ('push' == $name) { assertArgs(2, $args, $name); return $args[0] . '[] = ' . $args[1] . '; $$ = ' . $args[0]; } if ('pushNormalizing' == $name) { assertArgs(2, $args, $name); return 'if (is_array(' . $args[1] . ')) { $$ = array_merge(' . $args[0] . ', ' . $args[1] . '); }' . ' else { ' . $args[0] . '[] = ' . $args[1] . '; $$ = ' . $args[0] . '; }'; } if ('toArray' == $name) { assertArgs(1, $args, $name); return 'is_array(' . $args[0] . ') ? ' . $args[0] . ' : array(' . $args[0] . ')'; } if ('parseVar' == $name) { assertArgs(1, $args, $name); return 'substr(' . $args[0] . ', 1)'; } if ('parseEncapsed' == $name) { assertArgs(3, $args, $name); return 'foreach (' . $args[0] . ' as $s) { if ($s instanceof Node\\Scalar\\EncapsedStringPart) {' . ' $s->value = Node\\Scalar\\String_::parseEscapeSequences($s->value, ' . $args[1] . ', ' . $args[2] . '); } }'; } if ('parseEncapsedDoc' == $name) { assertArgs(2, $args, $name); return 'foreach (' . $args[0] . ' as $s) { if ($s instanceof Node\\Scalar\\EncapsedStringPart) {' . ' $s->value = Node\\Scalar\\String_::parseEscapeSequences($s->value, null, ' . $args[1] . '); } }' . ' $s->value = preg_replace(\'~(\\r\\n|\\n|\\r)\\z~\', \'\', $s->value);' . ' if (\'\' === $s->value) array_pop(' . $args[0] . ');'; } return $matches[0]; }, $code); }
function resolveArrays($code) { return preg_replace_callback('~' . PARAMS . '~', function ($matches) { $elements = magicSplit('(?:' . PARAMS . '|' . ARGS . ')(*SKIP)(*FAIL)|,', $matches['params']); // don't convert [] to array, it might have different meaning if (empty($elements)) { return $matches[0]; } $elementCodes = array(); foreach ($elements as $element) { // convert only arrays where all elements have keys if (false === strpos($element, ':')) { return $matches[0]; } list($key, $value) = explode(':', $element, 2); $elementCodes[] = "'" . $key . "' =>" . $value; } return 'array(' . implode(', ', $elementCodes) . ')'; }, $code); }
return array_filter($pieces); } echo '<pre>'; //////////////////// //////////////////// //////////////////// list($defs, $ruleBlocks) = magicSplit('%%', file_get_contents(GRAMMAR_FILE)); if ('' !== trim(preg_replace(regex(RULE_BLOCK), '', $ruleBlocks))) { die('Not all rule blocks were properly recognized!'); } preg_match_all(regex(RULE_BLOCK), $ruleBlocks, $ruleBlocksMatches, PREG_SET_ORDER); foreach ($ruleBlocksMatches as $match) { $ruleBlockName = $match['name']; $rules = magicSplit('\\|', $match['rules']); foreach ($rules as &$rule) { $parts = magicSplit('\\s+', $rule); $usedParts = array(); foreach ($parts as $part) { if ('{' === $part[0]) { preg_match_all('~\\$([0-9]+)~', $part, $backReferencesMatches, PREG_SET_ORDER); foreach ($backReferencesMatches as $match) { $usedParts[$match[1]] = true; } } } $i = 1; foreach ($parts as &$part) { if ('/' === $part[0]) { continue; } if (isset($usedParts[$i])) {
foreach ($ruleBlocksMatches as $match) { $ruleBlockName = $match['name']; $rules = magicSplit('\\|', $match['rules']); foreach ($rules as &$rule) { $parts = magicSplit('\\s+', $rule); $usedParts = array(); foreach ($parts as $part) { if ('{' === $part[0]) { preg_match_all('~\\$([0-9]+)~', $part, $backReferencesMatches, PREG_SET_ORDER); foreach ($backReferencesMatches as $match) { $usedParts[$match[1]] = true; } preg_match_all('~(?<name>[A-Z][a-zA-Z]++)' . PARAMS . '~', $part, $nodeMatches, PREG_SET_ORDER); foreach ($nodeMatches as $match) { $signature =& $nodeSignatures[$match['name']]; $params = magicSplit('(?:' . PARAMS . '|' . ARGS . ')(*SKIP)(*FAIL)|,', $match['params']); if (!isset($signature)) { $signature = array(); foreach ($params as $i => $param) { list($name, ) = explode(': ', $param, 2); $signature[$i] = $name; } } else { foreach ($params as $i => $param) { list($name, ) = explode(': ', $param, 2); if ($signature[$i] != $name) { die('Signature mismatch for "' . $match['name'] . '"'); } } } }