%PDF- %PDF-
| Direktori : /www/varak.net/www.varak.net/texy/libs/ |
| Current File : /www/varak.net/www.varak.net/texy/libs/TexyParser.php |
<?php
/**
* Texy! - web text markup-language
* --------------------------------
*
* Copyright (c) 2004, 2008 David Grudl (http://davidgrudl.com)
*
* This source file is subject to the GNU GPL license that is bundled
* with this package in the file license.txt.
*
* For more information please see http://texy.info
*
* @copyright Copyright (c) 2004, 2008 David Grudl
* @license GNU GENERAL PUBLIC LICENSE version 2 or 3
* @link http://texy.info
* @package Texy
* @version $Id: TexyParser.php 218 2008-09-05 05:35:57Z David Grudl $
*/
/**
* Texy parser base class.
*
* @author David Grudl
* @copyright Copyright (c) 2004, 2008 David Grudl
* @package Texy
*/
class TexyParser extends TexyObject
{
/** @var Texy */
protected $texy;
/** @var TexyHtml */
protected $element;
/** @var array */
public $patterns;
/**
* @return Texy
*/
public function getTexy()
{
return $this->texy;
}
}
/**
* Parser for block structures.
*/
class TexyBlockParser extends TexyParser
{
/** @var string */
private $text;
/** @var int */
private $offset;
/** @var bool */
private $indented;
/**
* @param Texy
* @param TexyHtml
*/
public function __construct(Texy $texy, TexyHtml $element, $indented)
{
$this->texy = $texy;
$this->element = $element;
$this->indented = (bool) $indented;
$this->patterns = $texy->getBlockPatterns();
}
public function isIndented()
{
return $this->indented;
}
// match current line against RE.
// if succesfull, increments current position and returns TRUE
public function next($pattern, &$matches)
{
$matches = NULL;
$ok = preg_match(
$pattern . 'Am', // anchored & multiline
$this->text,
$matches,
PREG_OFFSET_CAPTURE,
$this->offset
);
if ($ok) {
$this->offset += strlen($matches[0][0]) + 1; // 1 = "\n"
foreach ($matches as $key => $value) $matches[$key] = $value[0];
}
return $ok;
}
public function moveBackward($linesCount = 1)
{
while (--$this->offset > 0)
if ($this->text{ $this->offset-1 } === "\n") {
$linesCount--;
if ($linesCount < 1) break;
}
$this->offset = max($this->offset, 0);
}
public static function cmp($a, $b)
{
if ($a[0] === $b[0]) return $a[3] < $b[3] ? -1 : 1;
if ($a[0] < $b[0]) return -1;
return 1;
}
/**
* @param string
* @return void
*/
public function parse($text)
{
$tx = $this->texy;
$tx->invokeHandlers('beforeBlockParse', array($this, & $text));
// parser initialization
$this->text = $text;
$this->offset = 0;
// parse loop
$matches = array();
$priority = 0;
foreach ($this->patterns as $name => $pattern)
{
preg_match_all(
$pattern['pattern'],
$text,
$ms,
PREG_OFFSET_CAPTURE | PREG_SET_ORDER
);
foreach ($ms as $m) {
$offset = $m[0][1];
foreach ($m as $k => $v) $m[$k] = $v[0];
$matches[] = array($offset, $name, $m, $priority);
}
$priority++;
}
unset($name, $pattern, $ms, $m, $k, $v);
usort($matches, array(__CLASS__, 'cmp')); // generates strict error in PHP 5.1.2
$matches[] = array(strlen($text), NULL, NULL); // terminal cap
// process loop
$el = $this->element;
$cursor = 0;
do {
do {
list($mOffset, $mName, $mMatches) = $matches[$cursor];
$cursor++;
if ($mName === NULL) break;
if ($mOffset >= $this->offset) break;
} while (1);
// between-matches content
if ($mOffset > $this->offset) {
$s = trim(substr($text, $this->offset, $mOffset - $this->offset));
if ($s !== '') {
$tx->paragraphModule->process($this, $s, $el);
}
}
if ($mName === NULL) break; // finito
$this->offset = $mOffset + strlen($mMatches[0]) + 1; // 1 = \n
$res = call_user_func_array(
$this->patterns[$mName]['handler'],
array($this, $mMatches, $mName)
);
if ($res === FALSE || $this->offset <= $mOffset) { // module rejects text
// asi by se nemelo stat, rozdeli generic block
$this->offset = $mOffset; // turn offset back
continue;
} elseif ($res instanceof TexyHtml) {
$el->insert(NULL, $res);
} elseif (is_string($res)) {
$el->insert(NULL, $res);
}
} while (1);
}
}
/**
* Parser for single line structures.
*/
class TexyLineParser extends TexyParser
{
/** @var bool */
public $again;
/**
* @param Texy
* @param TexyHtml
*/
public function __construct(Texy $texy, TexyHtml $element)
{
$this->texy = $texy;
$this->element = $element;
$this->patterns = $texy->getLinePatterns();
}
/**
* @param string
* @return void
*/
public function parse($text)
{
$tx = $this->texy;
// initialization
$pl = $this->patterns;
if (!$pl) {
// nothing to do
$this->element->insert(NULL, $text);
return;
}
$offset = 0;
$names = array_keys($pl);
$arrMatches = $arrOffset = array();
foreach ($names as $name) $arrOffset[$name] = -1;
// parse loop
do {
$min = NULL;
$minOffset = strlen($text);
foreach ($names as $index => $name)
{
if ($arrOffset[$name] < $offset) {
$delta = ($arrOffset[$name] === -2) ? 1 : 0;
if (preg_match($pl[$name]['pattern'],
$text,
$arrMatches[$name],
PREG_OFFSET_CAPTURE,
$offset + $delta)
) {
$m = & $arrMatches[$name];
if (!strlen($m[0][0])) continue;
$arrOffset[$name] = $m[0][1];
foreach ($m as $keyx => $value) $m[$keyx] = $value[0];
} else {
// try next time
continue;
}
} // if
if ($arrOffset[$name] < $minOffset) {
$minOffset = $arrOffset[$name];
$min = $name;
}
} // foreach
if ($min === NULL) break;
$px = $pl[$min];
$offset = $start = $arrOffset[$min];
$this->again = FALSE;
$res = call_user_func_array(
$px['handler'],
array($this, $arrMatches[$min], $min)
);
if ($res instanceof TexyHtml) {
$res = $res->toString($tx);
} elseif ($res === FALSE) {
$arrOffset[$min] = -2;
continue;
}
$len = strlen($arrMatches[$min][0]);
$text = substr_replace(
$text,
(string) $res,
$start,
$len
);
$delta = strlen($res) - $len;
foreach ($names as $name) {
if ($arrOffset[$name] < $start + $len) $arrOffset[$name] = -1;
else $arrOffset[$name] += $delta;
}
if ($this->again) {
$arrOffset[$min] = -2;
} else {
$arrOffset[$min] = -1;
$offset += strlen($res);
}
} while (1);
$this->element->insert(NULL, $text);
}
}