< ?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; } } ?>