< ?php
/**
* Documentar esto
*
* autor: gerardooscarjt@gmail.com
* fecha: 13/04/2011
* Uso típico:
* $parse = TreeScript::getParse($codigo);
* print_r($parse->getTokens());
*/
class TreeScript {
private $encoding;
private $pos;
private $len;
private $code;
private $tokens = array();
private $token = null;
private $last_key = null;
private $equal = false;
private $errors = array();
private $line = 1;
/**
* Documentar esto
*/
private function __construct($code, $encoding='UTF-8') {
Profiling::start("TreeScript __construct");
$this->encoding = $encoding;
$this->pos = 0;
$this->code = $code;
$this->len = mb_strlen($code, $this->encoding);
$this->parse();
Profiling::end();
}
/**
* Documentar esto
*/
public function getParse($code) {
return new TreeScript($code);
}
/**
* Documentar esto
*/
private function parse() {
$asignacion = '( *[:|=] *)';
$sin_comillas = '[^ ]*';
$comillas_simples = "'[^']*'";
$comillas_dobles = '"[^"]*"';
$atributos = "(([^ |^=|^:]*)$asignacion($comillas_dobles|$comillas_simples|$sin_comillas))"; // FUNCIONA DE PUTA MADREEEERL
$info1 = array();
$pattern1 = '((([^\[]|\[(?!\[))*)(\[\[([^ |^\]\]]*)([^\]\]]*)\]\])*(([^\[]|\[(?!\[))*))'; // Obtengo los tokens de texto, los tags y cadenas de atributos
preg_match_all($pattern1, $this->code, $info1, PREG_SET_ORDER );
foreach($info1 as $i) {
if (strlen($i[1])) {
$this->tokens[] = array(
'type'=>'text',
'data'=>$i[1]
);
}
if (strlen($i[4])) {
$info2 = array();
preg_match_all($atributos, $i[5], $info2, PREG_SET_ORDER);
$data = array();
foreach ($info2 as $i2) {
$key = $i2[1];
$value = $i2[3];
if ($value[0]=='"') {
$value = trim($value, '"');
} else if ($value[0]=="'") {
$value = trim($value, "'");
}
$data[$key] = $value;
}
$this->tokens[] = array(
'type'=>'token',
'data'=>$data,
'name'=>$i[4]
);
}
if (strlen($i[6])) {
$this->tokens[] = array(
'type'=>'text',
'data'=>$i[6]
);
}
}
return true;
}
private function parse_old() {
$state = 0;
$this->tokens = array();
$this->token = array('type'=>'text', 'data'=>'');
$key = null;
while (null !== $c = $this->getc()) {
if ($c == "\n") $this->line++;
switch ($state) {
case 0:
if ($c == '[') {
$state = 1;
} else if ($c == '{') {
$state = 9;
} else {
$this->token['data'] .= $c;
}
break;
case 1:
if ($c == '[') {
$this->tokens[] = $this->token;
$this->token = array('type'=>'token', 'name'=>'', 'data'=>array());
$state = 2;
} else {
$this->token['data'] .= '['.$c;
$state = 0;
}
break;
case 2:
if ($this->is_blank($c) || $c == ']') {
$this->error('Se esperaba un identificador justo después de \'[[\'');
} else {
$this->token['name'] = $c;
$state = 20;
}
break;
case 20:
if ($this->is_blank($c)) {
$state = 5;
} else if ($c == ']') {
$state = 4;
} else {
$this->token['name'] .= $c;
}
break;
case 4:
if ($c == ']') {
$this->tokens[] = $this->token;
$this->token = array('type'=>'text', 'data'=>'');
$state = 0;
} else {
$this->error('Se esperaba \']\'');
$state = 0;
}
break;
case 5:
if ($this->is_blank($c)) {
// No hago nada
} else if ($c==']') {
$state = 4;
} else if ($c=="'") {
$state = 7;
} else if ($c=='"') {
$state = 8;
} else if ($c=='=' || $c==':') {
if ($this->equal || $this->last_key == null) {
$this->error('No se permiten dos asignaciones seguidas (\'=\' o \':\')');
} else {
$this->equal = true;
}
} else {
$key = $c;
$state = 6;
}
break;
case 6:
if ($this->is_blank($c)) {
$this->addKey($key);
$state = 5;
} else if ($c == ']') {
$this->addKey($key);
$state = 4;
} else if ($c == '=' || $c == ':') {
$this->addKey($key);
$state = 5;
if ($this->equal || $this->last_key == null) {
$this->error('No se permiten dos asignaciones seguidas (\'=\' o \':\')');
} else {
$this->equal = true;
}
}else {
$key .= $c;
}
break;
case 7:
if ($c=="'") {
$this->addKey($key);
$state = 5;
} else {
$key .= $c;
}
break;
case 8:
if ($c=='"') {
$this->addKey($key);
$state = 5;
} else {
$key .= $c;
}
break;
case 11:
if ($c =='}') {
$this->tokens[] = $this->token;
$this->token = array('type'=>'text', 'data'=>'');
} else {
$this->error('Se esperaba una llave de cierre de comentario adicional \'}\'');
}
$state = 0;
break;
case 10:
if ($c == '}') {
$state = 11;
} else {
$this->token['data'] .= $c;
}
break;
case 9:
if ($c == '{') {
$this->tokens[] = $this->token;
$this->token = array('type'=>'comment', 'data'=>'');
$state = 10;
} else {
$this->token['data'] .= '{'.$c;
$state = 0;
}
break;
}
}
if ($this->token['type'] == 'text') {
$this->tokens[] = $this->token;
} else {
//ERROR, has dejado un token abierto !!!
$this->error('Hay una etiqueta abierta, falta de cerrar con \']]\'');
return false;
}
return true;
}
/**
* Documentar esto
*/
public function &getErrors() {
if (count($this->errors))
return $this->errors;
return false;
}
/**
* Documentar esto
*/
private function error($error) {
$this->errors[] = 'ERROR (line '.$this->line.'): '.$error;
}
/**
* Documentar esto
*/
private function addKey(&$key) {
if ($this->equal) {
$this->token['data'][$this->last_key] = $key;
$this->last_key = null;
} else {
$this->token['data'][$key] = null;
$this->last_key = $key;
}
$this->equal = false;
$key = '';
}
/**
* Documentar esto
*/
private function is_letterordigit($c) {
return in_array($c, self::$letterordigit);
}
/**
* Documentar esto
*/
private function is_blank($c) {
return $c == "\n" || $c == "\c" || $c == "\t" || $c == " ";
}
/**
* Documentar esto
*/
private function getc() {
if ($this->pos < $this->len) {
$c = mb_substr($this->code, $this->pos, 1, $this->encoding);
//$c = $this->code[$this->pos];
$this->pos++;
return $c;
} else {
return null;
}
}
/**
* Documentar esto
*/
public function &getTokens() {
return $this->tokens;
}
}
?>