function calc($x)
{
// указатель в строке. В комбинации с preg_match мы тут имеем бинарное смещение в строке - 8-bit
$start = 0;
/**
* Выдаем ошибку. Эхом. Если не нравится - можно раскоментировать Exception
* @param $msg
*/
$error =function($msg) use ($x,&$start){
// вот так находим место ошибки. Примерно... substr работает как он хочет, в зависимости от версии php, так что mb_...
$msg.="\n".mb_substr($x,0,$start, '8bit').'^^^'.mb_substr($x,$start,NULL , '8bit');
echo $msg; /* or
throw new Exception($msg);
*/
return null;
};
/**
* тип последнего отданного нам токена
*/
$lastTokenIsString=0;
/**
* Токенизация, функция, выдающая следующий токен из строки. Почти yeld, только не yeld...
*/
$getNextToken = function() use($x,&$error,&$start, &$lastTokenIsString){
if ($start < strlen($x)) {
if (!preg_match('/\s*(?:([^\}\{\|]+)|\}|\{|\|)/u', $x, $m, 0, $start))
return null;
if ('' == $m[0]) {
return $error('WTF? ошибка в регулярке?'); // системная ошибка, исправлять регулярку...
}
$start += mb_strlen($m[0], '8bit'); // в 7.3 регулярки работают с utf как с бинарными строками.
$lastTokenIsString = !empty($m[1]);
return trim($m[0]);
}
return null;
};
/**
* свертка токенов в результат
* сюда попадаем, когда слопали токен `{`
* @return array|mixed
*/
$getBracers = function () use (&$getBracers, &$getNextToken, &$error, &$lastTokenIsString) {
$result = [];
do {
$value = $getNextToken();
if($lastTokenIsString) {
$next= $getNextToken();
} else { // вместо текстовой строки указана операция
$next = $value;
$value = null;
}
if(is_null($next)) {
return $error('Что-то пошло не так. Скобки не закрыты ?');
}
if(!empty($value))
$result[]=['value'=>$value];
if ($next == '}') {
return $result;
} else if ($next == '|') {
} else if ($next == '{') {
$last=count($result) - 1;
if(!isset($result[$last]['child']))
$result[$last]['child']=[];
$result[$last]['child'] = array_merge(
$result[$last]['child'],$getBracers()
);
}
} while(true);
return $result;
};
/**
* расставляем проценты
*/
$percents=function(&$array,$pers=100) use(&$percents){
if(!empty($array))
foreach($array as &$a){
$a['percent']=$pers/count($array);
if(isset($a['child'])){
$percents($a['child'],$a['percent']);
}
}
};
// если в начале строки есть какой-то мусор - вот тут можно его поскипать...
if('{'!=$getNextToken()){
return $error('строка начинается не с символа {');
}
$result=$getBracers();
$percents($result);
return $result;
}
print_r(calc('{ 1 уровень - 1 | 1 уровень - 2 { 2 уровень - 1 | 2 уровень - 2 }}'));
// чилды в одной ячейке собираются в кучку
print_r(calc('{ 1 уровень - 1 | 1 уровень - 2 { 2 уровень - 1 | 2 уровень - 2 {aaaaaaaa}{aaaaaaaa}{aaaaaaaa} }}'));