Skip to content

Instantly share code, notes, and snippets.

@jubianchi
Last active December 30, 2015 18:48
Show Gist options
  • Select an option

  • Save jubianchi/7869581 to your computer and use it in GitHub Desktop.

Select an option

Save jubianchi/7869581 to your computer and use it in GitHub Desktop.
Mathcing
<?php
$rules = array(
new rule(
array('1', '2', '3'),
array("\033[33m" . '[\1, ', '\2, ', '\3' . "]\033[0m")
),
new rule(
array('3', '2', '1'),
array("\033[33m" . '[\1, ', '\2, ', '\3' . "]\033[0m")
),
new rule(
array('2', '3', '4'),
array("\033[33m" . '[\1, ', '\2, ', '\3' . "]\033[0m")
),
new rule(
array('6', '...', '9'),
array("\033[33m" . '[\1, ', '\2, ', '\3' . "]\033[0m")
),
new rule(
array('7', '...', '9'),
array("\033[33m" . '[\1, ', '\2, ', '\3' . "]\033[0m")
),
new rule(
array('1', '...', '3', '...', '6'),
array("\033[33m" . '[\1, ', '\2, ', '\3, ', '\4, ', '\5' . "]\033[0m")
)
);
$tokens = array('1', '2', '3', '4', '5', '6', '3', '2', '1', '2', '3', '6', '7', '8', '8', '9', '9');
echo "Matching sequence: \033[33m[" . implode(', ', $tokens) . "]\033[0m" . PHP_EOL . PHP_EOL;
foreach ($rules as $rule)
{
echo $rule . ': ' . $rule->apply($tokens) . PHP_EOL;
}
<?php
class rule
{
const TOKEN_FULL = -1;
const TOKEN_ID = 0;
const TOKEN_VALUE = 1;
const CONTEXT_FILE = 0;
const CONTEXT_FUNCTION = 2;
const CONTEXT_CLASS = 4;
const CONTEXT_METHOD = 8;
const CONTEXT_CLOSURE = 16;
protected $pattern;
protected $replacement;
protected $required;
protected $excluded;
protected $currentTokens;
protected $currentContext;
protected $currentPosition;
public function __construct(array $pattern, array $replacement, $required = null, $excluded = null)
{
$this->pattern = array_values($pattern);
$this->replacement = array_values($replacement);
$this->length = count($this->pattern);
$this->required = $required;
$this->excluded = $excluded;
}
public function __toString()
{
return implode('', $this->pattern);
}
public function apply(array $tokens)
{
$result = '';
$this->currentTokens = $tokens;
$tokensCount = count($this->currentTokens);
for ($tokensOffset = 0; $tokensOffset < $tokensCount; $tokensOffset++)
{
$ruleMatches = array();
$this->start($tokensOffset, $tokens);
try
{
if ($this->required !== null)
{
while (!($this->context() & $this->required) || ($this->excluded !== null && ($this->context() & $this->excluded)))
{
$result .= $this->current(self::TOKEN_VALUE);
$this->forward($tokens);
$tokensOffset++;
}
}
for ($patternOffset = 0; $patternOffset < $this->length; $patternOffset++)
{
$currentPatternToken = $this->pattern[$patternOffset];
if ($currentPatternToken === '...')
{
$nextPatternToken = $this->pattern[$patternOffset + 1];
$currentFill = '';
while ($this->current(self::TOKEN_VALUE) !== $nextPatternToken)
{
$currentFill .= $this->current(self::TOKEN_VALUE);
$this->forward($tokens);
}
if ($nextPatternToken !== $this->current(self::TOKEN_VALUE))
{
break;
}
$ruleMatches[] = $currentFill;
$patternOffset++;
$currentPatternToken = $this->pattern[$patternOffset];
}
if ($currentPatternToken === $this->current(self::TOKEN_VALUE) || $currentPatternToken === $this->current(self::TOKEN_ID))
{
$ruleMatches[] = $this->current(self::TOKEN_VALUE);
$this->forward($tokens);
}
else
{
break;
}
}
}
catch (\outOfBoundsException $e) {}
if (count($ruleMatches) === $this->length)
{
$result .= $this->replace($ruleMatches);
$tokensOffset = $this->currentPosition;
}
if (isset($tokens[$tokensOffset]))
{
$currentToken = $this->token($tokensOffset);
$result .= $currentToken[1];
}
}
return $result;
}
public function start($offset, array $tokens)
{
$this->currentPosition = $offset - 1;
$this->forward($tokens);
return $this;
}
public function forward(array $tokens)
{
$this->currentPosition++;
if ($this->currentPosition === count($tokens))
{
throw new \outOfBoundsException();
}
$this->context($this->current());
return $this;
}
public function current($return = self::TOKEN_FULL)
{
$token = $this->token($this->currentPosition);
if ($return !== self::TOKEN_FULL)
{
$token = $token[$return];
}
return $token;
}
public function context(array $token = null)
{
if(null === $token)
{
return $this->currentContext;
}
switch (true)
{
case ($token[self::TOKEN_ID] === T_CLASS && !($this->currentContext & self::CONTEXT_CLASS)):
//var_dump(sprintf('Entering CONTEXT_CLASS: %s - %d', $token[self::TOKEN_ID], self::CONTEXT_CLASS));
return $this->currentContext = self::CONTEXT_CLASS;
case ($token[self::TOKEN_ID] === T_FUNCTION && ($this->currentContext & self::CONTEXT_CLASS) && !($this->currentContext & self::CONTEXT_METHOD)):
//var_dump(sprintf('Entering CONTEXT_METHOD: %s - %d', $token[self::TOKEN_ID], $this->currentContext | self::CONTEXT_METHOD));
return $this->currentContext = $this->currentContext | self::CONTEXT_METHOD;
case ($token[self::TOKEN_ID] === T_FUNCTION && (($this->currentContext & self::CONTEXT_FUNCTION) || ($this->currentContext & self::CONTEXT_METHOD))):
//var_dump(sprintf('Entering CONTEXT_CLOSURE: %s - %d', $token[self::TOKEN_ID], $this->currentContext | self::CONTEXT_CLOSURE));
return $this->currentContext = $this->currentContext | self::CONTEXT_CLOSURE;
case ($token[self::TOKEN_ID] === T_FUNCTION):
//var_dump(sprintf('Entering CONTEXT_FUNCTION: %s - %d', $token[self::TOKEN_ID], $this->currentContext | self::CONTEXT_FUNCTION));
return $this->currentContext = $this->currentContext | self::CONTEXT_FUNCTION;
case ($token[self::TOKEN_VALUE] === '}' && $this->currentContext & self::CONTEXT_CLOSURE):
//var_dump(sprintf('Leaving CONTEXT_CLOSURE: %s - %d', $token[self::TOKEN_VALUE], $this->currentContext ^ self::CONTEXT_CLOSURE));
return $this->currentContext = $this->currentContext ^ self::CONTEXT_CLOSURE;
case ($token[self::TOKEN_VALUE] === '}' && $this->currentContext & self::CONTEXT_METHOD):
//var_dump(sprintf('Leaving CONTEXT_METHOD: %s - %d', $token[self::TOKEN_VALUE], $this->currentContext ^ self::CONTEXT_METHOD));
return $this->currentContext = $this->currentContext ^ self::CONTEXT_METHOD;
case ($token[self::TOKEN_VALUE] === '}' && $this->currentContext & self::CONTEXT_FUNCTION):
//var_dump(sprintf('Leaving CONTEXT_FUNCTION: %s - %d', $token[self::TOKEN_VALUE], $this->currentContext ^ self::CONTEXT_FUNCTION));
return $this->currentContext = $this->currentContext ^ self::CONTEXT_FUNCTION;
case ($token[self::TOKEN_VALUE] === '}' && $this->currentContext & self::CONTEXT_CLASS):
//var_dump(sprintf('Leaving CONTEXT_CLASS: %s - %d', $token[self::TOKEN_VALUE], $this->currentContext ^ self::CONTEXT_CLASS));
return $this->currentContext = $this->currentContext ^ self::CONTEXT_CLASS;
default:
return $this->currentContext = $this->currentContext ?: self::CONTEXT_FILE;
}
}
public function token($index)
{
$token = $this->currentTokens[$index];
if (is_array($token) === false)
{
$token = array(
$token,
$token
);
}
return $token;
}
public function replace(array $matches)
{
$replacements = '';
foreach ($this->replacement as $replacement)
{
if (preg_match('/(.*?)\\\(\d+)(.*)/', $replacement, $m) > 0)
{
$replacement = $m[1] . $matches[$m[2] - 1] . $m[3];
}
$replacements .= $replacement;
}
return $replacements;
}
}
<?php
require_once __DIR__ . '/rule.php';
require_once $argv[1];
<?php
$rules = array(
new rule(
array(T_RETURN, '...', ';'),
array('markLine(__LINE__);', 'return', '\2', ';')
),
new rule(
array('function', '...', '(', '...', ')', '...', '{'),
array('function', '\2', '(', '\4', ')', '\6', '{', 'moleExists(__CLASS__, __FUNCTION__);'),
rule::CONTEXT_METHOD,
rule::CONTEXT_CLOSURE
),
new rule(
array('function', '...', '(', '...', ')', '...', '{'),
array('function', '\2', '(', '\4', ')', '\6', '{', 'moleExists(__FUNCTION__);'),
rule::CONTEXT_FUNCTION,
rule::CONTEXT_CLOSURE
),
new rule(
array('function', '...', '(', '...', ')', '...', '{'),
array('function', '\2', '(', '\4', ')', '\6', '{', 'moleExists($this);'),
rule::CONTEXT_CLOSURE
)
);
$source = <<<'PHP'
<?php
function foo()
{
return rand(0, PHP_INT_MAX);
}
function bar()
{
return function() {
rand(0, PHP_INT_MAX);
};
}
class foo
{
public function bar($a, $b)
{
$a += 12;
$b = $b ?: rand(0, 10);
return $a + $b;
}
public function baz($a, $b)
{
return $a . $b;
}
public function boo($a, $b)
{
return function () {
return md5(uniqid()));
};
}
}
return $foo;
PHP;
$tokens = token_get_all($source);
foreach ($rules as $rule)
{
$source = $rule->apply($tokens);
$tokens = token_get_all($source);
}
echo $source;
@jubianchi
Copy link
Author

Running

php runner.php numbers.php

Will output

123: [1, 2, 3]45632[1, 2, 3]678899
321: 123456[3, 2, 1]23678899
234: 1[2, 3, 4]5632123678899
6...9: 12345[6, 321236788, 9]9
7...9: 123456321236[7, 88, 9]9
1...3...6: [1, 2, 3, 45, 6]32[1, 2, 3, , 6]78899

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment