<?php


/**
 * ソースをブロック要素としてパースする。
 * 
 * @param	string	$source	ソース
 * @param	string	$pagename	$pagenameとして解釈する。ページを指定できない場合は空文字列を渡す。
 * @return	T_Body
 */
function parse_block($source, $pagename)
{
	if(is_array($source)){
		$source = join("\n", $source);
	}
	$source = mb_ereg_replace('\r', '', $source);
	return T_Body::parse($source, new Context(resolvepath($pagename)));
}


/**
 * ソースをインライン要素としてパースする。
 * 
 * @param	string	$source	ソース
 * @param	string	$pagename	$pagenameとして解釈する。ページを指定できない場合は空文字列を渡す。
 * @return	T_Body
 */
function parse_inline($source, $pagename)
{
	if(is_array($source)){
		$source = join("\n", $source);
	}
	$source = rtrim(mb_ereg_replace('\r', '', $source));
	return T_Line::parse($source, new Context(resolvepath($pagename)));
}



/**
 * 文脈としての情報を保持する構造体。
 */
class Context
{
	var $pagename;	//起点となるページ名。
	
	
	function __construct($pagename)
	{
		$this->pagename = $pagename;
	}
}



/**
 * 内部表現の要素を表すクラス。
 */
abstract class T_Element
{
	protected $source;	//パース元ソース
	protected $elements = array();	//内包する要素
	
	protected $parent = null;	//上の要素への参照
	protected $prev = null;	//前の要素への参照
	protected $next = null;	//次の要素への参照
	
	function getsource(){ return $this->source; }
	function getelements(){ return $this->elements; }
	function getelem(){ return $this->elements[0]; }
	function getparent(){ return $this->parent; }
	function getprev(){ return $this->prev; }
	function getnext(){ return $this->next; }
	
	
	/**
	 * 内包する要素を保存する。
	 * 
	 * @param	T_Element	$elem
	 */
	protected function addelement($elem)
	{
		$elem->parent = $this;
		$this->elements[] = $elem;
		$last = count($this->elements) - 1;
		if($last != 0){
			$this->elements[$last-1]->next = $this->elements[$last];
			$this->elements[$last]->prev = $this->elements[$last-1];
		}
	}
	
	
	/**
	 * Visitorに要素を実行させる。
	 * 
	 * @param	Visitor	$v
	 */
	function accept($v)
	{
		$call = 'visit' . get_class($this);
		return $v->$call($this);
	}
	
	
	/**
	 * パースしたものを元に戻す。
	 * 
	 * @param	string	&$source	パースした部分を戻される。
	 */
	function undo(&$source)
	{
		$source = $this->source . $source;
	}
	
	
	protected function __construct($source)
	{
		$this->source = $source;
	}
}



/**
 * ブロック型要素を表すクラス。
 */
abstract class T_BlockElement extends T_Element
{
	/**
	 * パースを行う。
	 * 
	 * @param	array(string)	&$source	パースできた部分は削除される。
	 * @param	Context	$context
	 * @return	Element	パースエラーの場合はnullを返す。
	 */
	public static abstract function parse(&$source, $context);
}



/**
 * インライン型要素を表すクラス。
 */
abstract class T_InlineElement extends T_Element
{
	/**
	 * パースを行う。
	 * 
	 * @param	string	&$source	パースできた部分は削除される。
	 * @param	Context	$context
	 * @return	Element	パースエラーの場合はnullを返す。
	 */
	public static abstract function parse(&$source, $context);
}



class T_Body extends T_BlockElement
{
	public static function parse(&$source, $context)
	{
		static $classlist = array('T_Empty', 'T_Block', 'T_Paragraph');
		
		$src = $source;
		$elements = array();
		while($source != ''){
			foreach($classlist as $class){
				$ret = eval("return $class::parse(\$source, \$context);");
				if($ret != null){
					$elements[] = $ret;
					break;
				}
			}
		}
		return new T_Body($src, $elements);
	}
	
	
	protected function __construct($source, $elements)
	{
		$this->source = $source;
		foreach($elements as $e){
			$this->addelement($e);
		}
	}
}



class T_Empty extends T_BlockElement
{
	public static function parse(&$source, $context)
	{
		if(mb_ereg('^[\t 　]*(?:\n|$)', $source, $m)){
			$source = _substr($source, strlen($m[0]));
			return new T_Empty($m[0]);
		}
		return null;
	}
}



abstract class T_Block extends T_BlockElement
{
	public static function parse(&$source, $context)
	{
		static $classlist = array('T_Heading', 'T_Horizon', 'T_Pre', 'T_BlockQuote', 'T_UL', 'T_OL', 'T_DL', 'T_Table', 'T_BlockPlugin', 'T_BlockTag', 'T_Comment');
		
		foreach($classlist as $class){
			$ret = eval("return $class::parse(\$source, \$context);");
			if($ret != null){
				return $ret;
			}
		}
		return null;
	}
}



class T_Heading extends T_Block
{
	protected $level;
	protected $pagename;
	
	function getlevel(){ return $this->level; }
	function getstring(){ return $this->elements[0]->getsource(); }
	function getpagename(){ return $this->pagename; }
	
	
	public static function parse(&$source, $context)
	{
		if(mb_ereg('^([*＊]{1,4})[\t 　]*(.+?)[\t 　]*(?:\n|$)', $source, $m)){
			$source = _substr($source, strlen($m[0]));
			return new T_Heading($m[0], mb_strlen($m[1]), T_Line::parse($m[2], $context), $context->pagename);
		}
		return null;
	}
	
	
	protected function __construct($source, $level, $subject, $pagename)
	{
		$this->source = $source;
		$this->level = $level;
		$this->addelement($subject);
		$this->pagename = $pagename;
	}
}



class T_Horizon extends T_Block
{
	public static function parse(&$source, $context)
	{
		if(mb_ereg('^[-－ー]{4}[\t 　]*(?:\n|$)', $source, $m)){
			$source = _substr($source, strlen($m[0]));
			return new T_Horizon($m[0]);
		}
		return null;
	}
}



class T_Pre extends T_Block
{
	protected $text;	//array
	
	function gettext(){ return $this->text; }
	
	
	public static function parse(&$source, $context)
	{
		if(mb_ereg('^(?:[ \t].*?(?:\n|$))+', $source, $m)){
			$source = _substr($source, strlen($m[0]));
			$text = mb_ereg_replace('^[ \t](.*?(?:\n|$))', "\\1", $m[0], 'm');
			return new T_Pre($m[0], $text);
		}
		return null;
	}
	
	
	protected function __construct($source, $text)
	{
		$this->source = $source;
		$this->text = $text;
	}
}



class T_BlockQuote extends T_Block
{
	public static function parse(&$source, $context)
	{
		if(mb_ereg('^(?:[>＞].*?(?:\n|$))+', $source, $m)){
			$source = _substr($source, strlen($m[0]));
			$text = mb_ereg_replace('^[>＞](.*?(?:\n|$))', "\\1", $m[0], 'm');
			return new T_BlockQuote($m[0], T_Body::parse($text, $context));
		}
		return null;
	}
	
	
	protected function __construct($source, $body)
	{
		$this->source = $source;
		$this->addelement($body);
	}
}



class T_UL extends T_Block
{
	public static function parse(&$source, $context)
	{
		if(mb_ereg('^(?:[-－・].*?(?:\n|$))+', $source, $m)){
			$source = _substr($source, strlen($m[0]));
			$text = mb_ereg_replace('^[-－・](.*?(?:\n|$))', "\\1", $m[0], 'm');
			return new T_UL($m[0], T_List::parse($text, $context));
		}
		return null;
	}
	
	
	protected function __construct($source, $list)
	{
		$this->source = $source;
		$this->addelement($list);
	}
}



class T_OL extends T_Block
{
	public static function parse(&$source, $context)
	{
		if(mb_ereg('^(?:[+＋].*?(?:\n|$))+', $source, $m)){
			$source = _substr($source, strlen($m[0]));
			$text = mb_ereg_replace('^[+＋](.*?(?:\n|$))', "\\1", $m[0], 'm');
			return new T_OL($m[0], T_List::parse($text, $context));
		}
		return null;
	}
	
	
	protected function __construct($source, $list)
	{
		$this->source = $source;
		$this->addelement($list);
	}
}



class T_List extends T_Block
{
	public static function parse(&$source, $context)
	{
		static $classlist = array('T_UL', 'T_OL', 'T_LI');
		
		$src = $source;
		$list = array();
		while($source != ''){
			foreach($classlist as $class){
				$ret = eval("return $class::parse(\$source, \$context);");
				if($ret != null){
					$list[] = $ret;
					break;
				}
			}
			if($ret = null){
				//ここに来てはまずい（無限ループ防止）
				throw new MyException('プログラムにバグがあります(T_List)');
			}
		}
		return new T_List($src, $list);
	}
	
	
	protected function __construct($source, $list)
	{
		$this->source = $source;
		foreach($list as $l){
			$this->addelement($l);
		}
	}
}



class T_LI extends T_Block
{
	public static function parse(&$source, $context)
	{
		if(mb_ereg('^(.*?)(?:\n|$)', $source, $m)){
			$source = _substr($source, strlen($m[0]));
			return new T_LI($m[0], T_Line::parse($m[1], $context));
		}
		return null;
	}
	
	
	protected function __construct($source, $line)
	{
		$this->source = $source;
		$this->addelement($line);
	}
}



class T_DL extends T_Block
{
	public static function parse(&$source, $context)
	{
		static $classlist = array('T_DT', 'T_DD');
		
		$src = $source;
		
		$elem[] = T_DT::parse($source, $context);
		if($elem[0] != null){
			while($source != array()){
				foreach($classlist as $class){
					$ret = eval("return $class::parse(\$source, \$context);");
					if($ret != null){
						$elem[] = $ret;
						break;
					}
				}
				if($ret == null){
					break;
				}
			}
			return new T_DL(_substr($src, 0, strlen($src) - strlen($source)), $elem);
		}
		return null;
	}
	
	
	protected function __construct($source, $elements)
	{
		$this->source = $source;
		foreach($elements as $e){
			$this->addelement($e);
		}
	}
}



class T_DT extends T_DL
{
	public static function parse(&$source, $context)
	{
		if(mb_ereg('^([:：])(.+?\1.*(?:\n|$))', $source, $m)){
			$mark = $m[1];
			$term = T_Line::parse($m[2], $context, $m[1]);
			if(mb_ereg("^{$mark}[\t 　]*\n?", $m[2], $n)){
				$src = $mark . $term->getsource() . $n[0];
				$source = _substr($source, strlen($src));
				return new T_DT($src, $term);
			}
		}
		return null;
	}
	
	
	protected function __construct($source, $term)
	{
		$this->source = $source;
		$this->addelement($term);
	}
}



class T_DD extends T_DL
{
	public static function parse(&$source, $context)
	{
		$src = $source;
		
		$ret = T_DT::parse($source, $context);
		if($ret != null){
			$ret->undo($source);
			return null;
		}
		$ret = T_Empty::parse($source, $context);
		if($ret != null){
			$ret->undo($source);
			return null;
		}
		
		$ret = T_Block::parse($source, $context);
		if($ret != null){
			return new T_DD(_substr($src, 0, strlen($src) - strlen($source)), $ret);
		}
		if(mb_ereg('^(.*?)(?:\n|$)', $source, $m)){
			$source = _substr($source, strlen($m[0]));
			$ret = T_Line::parse($m[1], $context);
			return new T_DD(_substr($src, 0, strlen($src) - strlen($source)), $ret);
		}
		//ここに来てはまずい（無限ループ防止）
		throw new MyException('プログラムにバグがあります(T_DD)');
	}
	
	
	protected function __construct($source, $elem)
	{
		$this->source = $source;
		$this->addelement($elem);
	}
}



class T_Table extends T_Block
{
	public static function parse(&$source, $context)
	{
		$src = $source;
		$elem[] = T_TR::parse($source, $context);
		if($elem[0] != null){
			while($source != ''){
				$e = T_TR::parse($source, $context);
				if($e == null){
					break;
				}
				if($e->getcols() != $elem[0]->getcols()){
					$e->undo($source);
					break;
				}
				$elem[] = $e;
			}
			return new T_Table(_substr($src, 0, strlen($src) - strlen($source)), $elem);
		}
		return null;
	}
	
	
	protected function __construct($source, $elements)
	{
		$this->source = $source;
		foreach($elements as $e){
			$this->addelement($e);
		}
	}
}



class T_TR extends T_Table
{
	protected $cols;
	
	function getcols(){ return $this->cols; }
	
	
	public static function parse(&$source, $context)
	{
		if(mb_ereg('^\|((?:[^\n]*?\|)+)([HhLlCcRr]*)[\t 　]*(?:\n|$)', $source, $m)){
			while($m[1] != ''){
				$elem[] = T_TD::parse($m[1], $context, $m[2]);
			}
			$source = _substr($source, strlen($m[0]));
			return new T_TR($m[0], $elem);
		}
		return null;
	}
	
	
	protected function __construct($source, $elements)
	{
		$this->source = $source;
		foreach($elements as $e){
			$this->addelement($e);
		}
		$this->cols = count($elements);
	}
}



class T_TD extends T_TR
{
	protected $align;
	protected $isheader;
	
	function getalign(){ return $this->align; }
	function isheader(){ return $this->isheader; }
	
	
	public static function parse(&$source, $context, $option = '')
	{
		$src = $source;
		$e = T_Line::parse($source, $context, '\|');
		if($e == null){
			return null;
		}
		if(!mb_ereg('^\|', $source)){
			$e->undo($source);
			return null;
		}
		$source = _substr($source, 1);
		return new T_TD(_substr($src, strlen($src) - strlen($source)), $e, $option);
	}
	
	
	protected function __construct($source, $elem, $option)
	{
		$this->source = $source;
		$this->addelement($elem);
		$this->align = null;
		$this->isheader = false;
		$this->setoption($option);
	}
	
	
	private function setoption($option)
	{
		for($i = 0; $i < strlen($option); $i++){
			switch(strtoupper($option{$i})){
				case 'H':
					$this->isheader = true;
					break;
				case 'L':
					$this->align = 'left';
					break;
				case 'C':
					$this->align = 'center';
					break;
				case 'R':
					$this->align = 'right';
					break;
			}
		}
	}
}



class T_BlockPlugin extends T_Block
{
	protected $pluginname;
	protected $param1;
	protected $param2;
	protected $pagename;
	
	function getpluginname(){ return $this->pluginname; }
	function getparam1(){ return $this->param1; }
	function getparam2(){ return $this->param2; }
	function getpagename(){ return $this->pagename; }
	
	
	public static function parse(&$source, $context)
	{
		if(mb_ereg('^#([a-zA-Z0-9_]+)(?:[\t 　]*\((.*?)\))?[\t 　]*\n?', $source, $m)){
			$pluginname = $m[1];
			$param1 = $m[2];
			$src = $m[0];
			$source = _substr($source, strlen($src));
			
			$param2 = '';
			if(mb_ereg('^{', $source)){
				$s = $_s = _substr($source, 1);
				while($s != ''){
					static $classlist = array('T_Empty', 'T_Block');
					foreach($classlist as $class){
						$ret = eval("return $class::parse(\$s, \$context);");
						if($ret != null){
							break;
						}
					}
					if($ret == null){
						T_Line::parse($s, $context, '[}\n]');
						if(mb_ereg('^}[\t 　]*\n?', $s, $m)){
							$len = strlen($source) - strlen($s) + strlen($m[0]);
							$src .= _substr($source, 0, $len);
							$source = _substr($source, $len);
							$param2 = _substr($_s, 0, strlen($_s) - strlen($s));
							break;
						}
					}
				}
			}
			
			return new T_BlockPlugin($src, $pluginname, $param1, $param2, $context->pagename);
		}
		return null;
	}
	
	
	protected function __construct($source, $pluginname, $param1, $param2, $pagename)
	{
		$this->source = $source;
		$this->pluginname = $pluginname;
		$this->param1 = $param1;
		$this->param2 = $param2;
		$this->pagename = $pagename;
	}
}



class T_BlockTag extends T_BlockPlugin
{
	public static function parse(&$source, $context)
	{
		if(mb_ereg('^<([a-zA-Z0-9_]+)[\t 　]*(.*?)?[\t 　]*(/?)>[\t 　]*\n?', $source, $m)){
			$pluginname = $m[1];
			$param1 = $m[2];
			$src = $m[0];
			$source = _substr($source, strlen($src));
			
			$param2 = '';
			if($m[3] != '/'){
				$s = $_s = $source;
				while($s != ''){
					static $classlist = array('T_Empty', 'T_Block');
					foreach($classlist as $class){
						$ret = eval("return $class::parse(\$s, \$context);");
						if($ret != null){
							break;
						}
					}
					if($ret == null){
						T_Line::parse($s, $context, "(?:</$pluginname>|\n)");
						if(mb_ereg("^</$pluginname>[\t 　]*\n?", $s, $m)){
							$len = strlen($source) - strlen($s) + strlen($m[0]);
							$src .= _substr($source, 0, $len);
							$source = _substr($source, $len);
							$param2 = _substr($_s, 0, strlen($_s) - strlen($s));
							break;
						}
					}
				}
			}
			
			return new T_BlockTag($src, $pluginname, $param1, $param2, $context->pagename);
		}
		return null;
	}
}



class T_Comment extends T_Block
{
	public static function parse(&$source, $context)
	{
		if(mb_ereg('^//.*?(?:\n|$)', $source, $m)){
			$source = _substr($source, strlen($m[0]));
			return new T_Comment($m[0]);
		}
		return null;
	}
}



class T_Paragraph extends T_Block
{
	public static function parse(&$source, $context)
	{
		static $classlist = array('T_Empty', 'T_Block');
		
		$src = $source;
		
		$line = array();
		while($source != ''){
			foreach($classlist as $class){
				$ret = eval("return $class::parse(\$source, \$context);");
				if($ret != null){
					$ret->undo($source);
					break 2;
				}
			}
			mb_ereg('^.*?(?:\n|$)', $source, $m);
			$source = _substr($source, strlen($m[0]));
			$str = mb_ereg_replace('^　', '', $m[0]);
			$line[] = T_Line::parse($str, $context);
		}
		return new T_Paragraph(_substr($src, 0, strlen($src) - strlen($source)), $line);
	}
	
	
	public function __construct($source, $lines)
	{
		$this->source = $source;
		foreach($lines as $l){
			$this->addelement($l);
		}
	}
}


class T_Line extends T_InlineElement
{
	public static function parse(&$str, $context, $terminator = '')
	{
		static $classlist = array('T_URL', 'T_Mail', 'T_BlacketName', 'T_InlinePlugin', 'T_Footnote', 'T_Strong');
		
		$backup = $str;
		$elements = array();
		while($str != ''){
			$len = strlen($str);
			foreach($classlist as $class){
				$ret = eval("return $class::getdistance(\$str, \$context);");
				if($ret == 0){
					$elements[] = eval("return $class::parse(\$str, \$context);");
					$len = 0;
					break;
				}
				if($ret < $len){
					$len = $ret;
				}
			}
			if($len > 0){
				$text = _substr($str, 0, $len);
				if($terminator != '' && mb_ereg("^(.*?)$terminator", $text, $m)){
					$str = _substr($str, strlen($m[1]));
					$elements[] = T_Text::parse($m[1], $context);
					break;
				}
				else{
					$str = _substr($str, $len);
					$elements[] = T_Text::parse($text, $context);
				}
			}
		}
		return new T_Line(_substr($backup, 0, strlen($backup) - strlen($str)), $elements);
	}
	
	
	public function __construct($source, $elements)
	{
		$this->source = $source;
		foreach($elements as $elem){
			$this->addelement($elem);
		}
	}
}



class T_URL extends T_InlineElement
{
	function geturl(){ return $this->source; }
	
	
	public static function getdistance(&$str, $context)
	{
		if(mb_ereg('^(.*?)' . EXP_URL, $str, $m)){
			return strlen($m[1]);
		}
		else{
			return strlen($str);
		}
	}
	
	
	public static function parse(&$str, $context)
	{
		if(mb_ereg('^' . EXP_URL, $str, $m)){
			$str = _substr($str, strlen($m[0]));
			return new T_URL($m[0]);
		}
		return null;
	}
}



class T_Mail extends T_InlineElement
{
	function getaddress(){ return $this->source; }
	
	
	public static function getdistance(&$str, $context)
	{
		if(mb_ereg('^(.*?)' . EXP_MAIL, $str, $m)){
			return strlen($m[1]);
		}
		else{
			return strlen($str);
		}
	}
	
	
	public static function parse(&$str, $context)
	{
		if(mb_ereg('^' . EXP_MAIL, $str, $m)){
			$str = _substr($str, strlen($m[0]));
			return new T_Mail($m[0]);
		}
		return null;
	}
}



class T_BlacketName extends T_InlineElement
{
	protected $pagename;
	protected $alias;
	protected $currentpage;
	
	function getpagename(){ return $this->pagename; }
	function getalias(){ return $this->alias; }
	function getcurrentpage(){ return $this->currentpage; }
	
	
	public static function getdistance(&$str, $context)
	{
		if(mb_ereg('^(.*?)\[\[.+?\]\]', $str, $m)){
			return strlen($m[1]);
		}
		else{
			return strlen($str);
		}
	}
	
	
	public static function parse(&$str, $context)
	{
		if(mb_ereg('^\[\[(.+?)\]\]', $str, $m)){
			$str = _substr($str, strlen($m[0]));
			if(mb_ereg('(.+?)>(.+)', $m[1], $match)){
				return new T_BlacketName($m[0], $match[2], $match[1], $context->pagename);
			}
			else{
				return new T_BlacketName($m[0], $m[1], '', $context->pagename);
			}
		}
		return null;
	}
	
	
	protected function __construct($source, $pagename, $alias, $currentpage)
	{
		$this->source = $source;
		$this->pagename = $pagename;
		$this->alias = $alias;
		$this->currentpage = $currentpage;
	}
}



class T_InlinePlugin extends T_InlineElement
{
	protected $pluginname;
	protected $param1;
	protected $param2;
	protected $pagename;
	
	function getpluginname(){ return $this->pluginname; }
	function getparam1(){ return $this->param1; }
	function getparam2(){ return $this->param2; }
	function getpagename(){ return $this->pagename; }
	
	
	public static function getdistance(&$str, $context)
	{
		if(mb_ereg('^(.*?)&[a-zA-Z0-9_]+[\t ]*\(.*?\)', $str, $m)){
			return strlen($m[1]);
		}
		else{
			return strlen($str);
		}
	}
	
	
	public static function parse(&$source, $context)
	{
		if(mb_ereg('^&([a-zA-Z0-9_]\w+)[\t ]*\((.*?)\)[\t 　]*', $source, $m)){
			$pluginname = $m[1];
			$param1 = $m[2];
			$src = $m[0];
			$source = _substr($source, strlen($src));
			
			$param2 = '';
			if(mb_ereg('^{', $source)){
				$s = $_s = _substr($source, 1);
				T_Line::parse($s, $context, '}');
				if(mb_ereg('^}', $s, $m)){
					$len = strlen($source) - strlen($s) + strlen($m[0]);
					$src .= _substr($source, 0, $len);
					$source = _substr($source, $len);
					$param2 = _substr($_s, 0, strlen($_s) - strlen($s));
				}
			}
			
			return new T_InlinePlugin($src, $pluginname, $param1, $param2, $context->pagename);
		}
		return null;
	}
	
	
	protected function __construct($source, $pluginname, $param1, $param2, $pagename)
	{
		$this->source = $source;
		$this->pluginname = $pluginname;
		$this->param1 = $param1;
		$this->param2 = $param2;
		$this->pagename = $pagename;
	}
}



class T_Footnote extends T_InlineElement
{
	public static function getdistance(&$str, $context)
	{
		$src = $str;
		while(mb_ereg('^(.*?)(\(\(|（（)', $src, $m)){
			$src = _substr($src, strlen($m[1]));
			$ret = T_Footnote::parse($src, $context);
			if($ret != null){
				$ret->undo($src);
				return strlen($str) - strlen($src);
			}
			$src = _substr($src, strlen($m[2]));
		}
		return strlen($str);
	}
	
	
	public static function parse(&$str, $context)
	{
		if(mb_ereg('^(?:\(\(|（（)', $str, $m)){
			$src = $str;
			$rdelim = $m[0] == '((' ? '\)\)' : '））';
			$str = _substr($str, strlen($m[0]));
			$ret = T_Line::parse($str, $context, $rdelim);
			if(mb_ereg("^{$rdelim}", $str, $m)){
				$str = _substr($str, strlen($m[0]));
				$src = _substr($src, 0, strlen($src) - strlen($str));
				return new T_Footnote($src, $ret);
			}
			$str = $src;
		}
		return null;
	}
	
	
	protected function __construct($source, $line)
	{
		$this->source = $source;
		$this->addelement($line);
	}
}



class T_Strong extends T_InlineElement
{
	protected $str;
	protected $level;
	
	function getstr(){ return $this->str; }
	function getlevel(){ return $this->level; }
	
	
	public static function getdistance(&$str, $context)
	{
		if(mb_ereg('^(.*?)[\t 　]?(\*\*?)[^\t 　]+?\2(?:[\t 　]|$|(?=\n))', $str, $m)){
			return strlen($m[1]);
		}
		else{
			return strlen($str);
		}
	}
	
	
	public static function parse(&$str, $context)
	{
		if(mb_ereg('^[\t 　]?(\*\*?)([^\t 　]+?)\1(?:[\t 　]|$|(?=\n))', $str, $m)){
			$str = _substr($str, strlen($m[0]));
			return new T_Strong($m[0], $m[2], strlen($m[1]));
		}
		return null;
	}
	
	
	protected function __construct($source, $str, $level)
	{
		$this->source = $source;
		$this->str = $str;
		$this->level = $level;
	}
}



class T_Text extends T_InlineElement
{
	public static function getdistance(&$str, $context)
	{
		return 0;
	}
	
	
	public static function parse(&$str, $context)
	{
		$src = $str;
		$elements = array();
		while($str != ''){
			$i = T_AutoLink::getdistance($str, $context);
			if($i == 0){
				$elements[] = T_AutoLink::parse($str, $context);
				continue;
			}
			
			$k = T_FuzzyLink::getdistance($str, $context);
			if($k == 0){
				$elements[] = T_FuzzyLink::parse($str, $context);
				continue;
			}
			
			$n = min($i, $k);
			$elements[] = T_String::parse(_substr($str, 0, $n), $context);
			$str = _substr($str, $n);
		}
		return new T_Text($src, $elements);
		
	}
	
	
	protected function __construct($source, $elements)
	{
		$this->source = $source;
		foreach($elements as $e){
			$this->addelement($e);
		}
	}
}



class T_String extends T_InlineElement
{
	function getstring(){ return $this->source; }
	
	
	public static function getdistance(&$str, $context)
	{
		return 0;
	}
	
	
	public static function parse(&$str, $context)
	{
		$ins = new T_String($str);
		$str = '';
		return $ins;
	}
}



class T_AutoLink extends T_InlineElement
{
	protected $pagename;
	
	function getpagename(){ return $this->pagename; }
	function getalias(){ return $this->source; }
	
	
	public static function getdistance(&$str, $context)
	{
		$list[] = $context->pagename;
		$list[] = getdirname($context->pagename);
		$list[] = '';
		
		$ret = strlen($str);
		foreach($list as $dir){
			$exp = AutoLink::getinstance()->getexpression($dir);
			if($exp != '' && mb_ereg("^(.*?)$exp", $str, $m)){
				$ret = min($ret, strlen($m[1]));
			}
		}
		return $ret;
	}
	
	
	public static function parse(&$str, $context)
	{
		$list[] = $context->pagename;
		$list[] = getdirname($context->pagename);
		$list[] = '';
		
		foreach($list as $dir){
			$exp = AutoLink::getinstance()->getexpression($dir);
			if($exp != '' && mb_ereg("^$exp", $str, $m)){
				$str = _substr($str, strlen($m[0]));
				$dir = $dir == '' ? '' : "{$dir}/";
				return new T_AutoLink($dir . $m[0], $m[0]);
			}
		}
		return null;
	}
	
	
	protected function __construct($pagename, $alias)
	{
		$this->pagename = $pagename;
		$this->source = $alias;
	}
}


/**
 * あいまいリンクされたページを表す。
 */
class T_FuzzyLink extends T_InlineElement
{
	function getkey(){ return $this->source; }
	
	
	public static function getdistance(&$str, $context)
	{
		$ret = strlen($str);
		$exp = FuzzyLink::getinstance()->getexpression();
		if($exp != '' && mb_eregi("^(.*?)$exp", $str, $m)){
			$ret = min($ret, strlen($m[1]));
		}
		return $ret;
	}
	
	
	public static function parse(&$str, $context)
	{
		$exp = FuzzyLink::getinstance()->getexpression();
		if($exp != '' && mb_eregi("^$exp", $str, $m)){
			$str = _substr($str, strlen($m[0]));
			return new T_FuzzyLink($m[0], $context);
		}
		return null;
	}
	
	
	protected function __construct($key)
	{
		$this->source = $key;
	}
}

?>