powered by nequal
Home » HatenaSyntax » Timeline » 464

Diffs

HatenaSyntax/trunk/code/HatenaSyntax/Element.php

@@ -1,36 +0,0 @@
-<?php
-
-// - Element := Header | DefinitionList | Table | List |
-//              BlockQuote | Pre | SuperPre | Line<-(optional=true)
-class HatenaSyntax_Element implements PEG_IParser
-{
-    protected $p;
-    function __construct()
-    {
-        $blockquote = HatenaSyntax_BlockQuote::getInstance();
-        $blockquote->setElement($this);
-        $line = new PEG_Sequence(array(new HatenaSyntax_Line(true), HatenaSyntax_NewLine::getInstance()));
-        $parser = new PEG_Choice();
-        $parser->with(new HatenaSyntax_Header)
-               ->with($blockquote)
-               ->with(new HatenaSyntax_DefinitionList)
-               ->with(new HatenaSyntax_Table)
-               ->with(new HatenaSyntax_List)
-               ->with(HatenaSyntax_Pre::getInstance())
-               ->with(HatenaSyntax_SuperPre::getInstance())
-               ->with(new PEG_CallbackAction(array($this, 'processLine'), $line));
-        $this->p = $parser;
-    }
-    function processLine($result)
-    {
-        return array('type' => 'line', 'body' => $result[0]);
-    }
-    function parse(PEG_IContext $c)
-    {
-        return $this->p->parse($c);
-    }
-    function processNewLine($result)
-    {
-        return array('type' => 'empty_line');
-    }
-}
\ ファイルの末尾に改行がありません

HatenaSyntax/trunk/code/HatenaSyntax/Parser.php

@@ -1,20 +0,0 @@
-<?php
-
-class HatenaSyntax_Parser
-{
-    protected $p;
-    function __construct()
-    {
-        $this->p = new PEG_Many(new HatenaSyntax_Element);
-    }
-    function parse($str)
-    {
-        try {
-            $context = new PEG_Context($str);
-            $result = $this->p->parse($context);
-        } catch (PEG_Failure $e) {
-            return false;
-        }
-        return $result;
-    }
-}
\ ファイルの末尾に改行がありません

HatenaSyntax/trunk/code/HatenaSyntax/Link.php

@@ -1,14 +0,0 @@
-<?php
-
-class HatenaSyntax_Link extends PEG_Choice
-{
-    function __construct()
-    {
-        parent::__construct(array(HatenaSyntax_HttpLink::getInstance()));
-    }
-    static function getInstance()
-    {
-        static $o = null;
-        return $o ? $o : $o = new self;
-    }
-}
\ ファイルの末尾に改行がありません

HatenaSyntax/trunk/code/HatenaSyntax/HttpLink.php

@@ -1,56 +0,0 @@
-<?php
-
-// - HttpLink := "[" ("http://" | "https://") ((?!"]" | ":title=") LineChar)+ (":title=" ((?!"]") LineChar)+)? "]"
-class HatenaSyntax_HttpLink extends PEG_Action
-{
-    function __construct()
-    {
-        $line_char = HatenaSyntax_LineChar::getInstance();
-
-        $url_elt = new PEG_Sequence();
-        $url_elt->with(new PEG_LookaheadNot(new PEG_Choice(array(PEG_Token::get(']'),
-                                                                 PEG_Token::get(':title=')))))
-                ->with($line_char);
-
-        $title_elt = new PEG_Sequence();
-        $title_elt->with(new PEG_LookaheadNot(PEG_Token::get(']')))
-                  ->with($line_char);
-
-        $title = new PEG_Sequence();
-        $title->with(PEG_Token::get(':title='))
-              ->with(new PEG_Many1($title_elt));
-
-        $parser = new PEG_Sequence();
-        $parser->with(PEG_Token::get('['))
-               ->with(new PEG_Choice(array(PEG_Token::get('http://'), PEG_Token::get('https://'))))
-               ->with(new PEG_Many1($url_elt))
-               ->with(new PEG_Optional($title))
-               ->with(PEG_Token::get(']'));
-        parent::__construct($parser);
-    }
-    function process($result)
-    {
-        $ret = $result[1];
-
-        foreach ($result[2] as $elt) {
-            $ret .= $elt[1];
-        }
-
-        if ($result[3]) {
-            $title = '';
-            foreach ($result[3][1] as $c) {
-                $title .= $c[1];
-            }
-        }
-        else {
-            $title = null;
-        }
-
-        return array('type' => 'link', 'title' => $title, 'body' => trim($ret));
-    }
-    static function getInstance()
-    {
-        static $o = null;
-        return $o ? $o : $o = new self;
-    }
-}
\ ファイルの末尾に改行がありません

HatenaSyntax/trunk/code/HatenaSyntax/Footnote.php

@@ -1,27 +0,0 @@
-<?php
-
-class HatenaSyntax_Footnote extends PEG_Action
-{
-    function __construct()
-    {
-        $content_elt = new PEG_Sequence();
-        $content_elt->with(new PEG_LookaheadNot(PEG_Token::get('))')))
-                    ->with(new PEG_Choice(array(HatenaSyntax_Link::getInstance(), HatenaSyntax_LineChar::getInstance())));
-        $parser = new PEG_Sequence();
-        $parser->with(PEG_Token::get('(('))
-               ->with(new PEG_Many1($content_elt))
-               ->with(PEG_Token::get('))'));
-        parent::__construct($parser);
-    }
-    function process($result)
-    {
-        $ret = '';
-        foreach ($result[1] as $elt) $ret .= $elt[1];
-        return array('type' => 'footnote', 'body' => $ret);
-    }
-    static function getInstance()
-    {
-        static $o = null;
-        return $o ? $o : $o = new self;
-    }
-}
\ ファイルの末尾に改行がありません

HatenaSyntax/trunk/code/HatenaSyntax/NewLine.php

@@ -1,16 +0,0 @@
-<?php
-
-class HatenaSyntax_NewLine extends PEG_Choice
-{
-    function __construct()
-    {
-        parent::__construct(array(PEG_Token::get("\r\n"),
-                                  PEG_Token::get("\n"),
-                                  PEG_Token::get("\r")));
-    }
-    static function getInstance()
-    {
-        static $o = null;
-        return $o ? $o : $o = new self;
-    }
-}
\ ファイルの末尾に改行がありません

HatenaSyntax/trunk/code/HatenaSyntax/LineElement.php

@@ -1,20 +0,0 @@
-<?php
-
-class HatenaSyntax_LineElement extends PEG_Action
-{
-    function __construct(PEG_IParser $arg_parser = null)
-    {
-        $item = new PEG_Choice(array(HatenaSyntax_Link::getInstance(),
-                                     HatenaSyntax_Footnote::getInstance(),
-                                     HatenaSyntax_LineChar::getInstance()));
-        $parser = new PEG_Sequence(array(is_null($arg_parser) ?
-                                         new PEG_Lookahead(PEG_Anything::getInstance()) :
-                                         new PEG_LookaheadNot($arg_parser),
-                                         $item));
-        parent::__construct($parser);
-    }
-    function process($result)
-    {
-        return $result[1];
-    }
-}
\ ファイルの末尾に改行がありません

HatenaSyntax/trunk/code/HatenaSyntax/EndOfLine.php

@@ -1,21 +0,0 @@
-<?php
-
-class HatenaSyntax_EndOfLine extends PEG_Action
-{
-    function __construct()
-    {
-        $p = new PEG_Choice(array(HatenaSyntax_NewLine::getInstance(),
-                                  PEG_EOS::getInstance()));
-        parent::__construct($p);
-    }
-    function process($result)
-    {
-        if ($result === false) $result = '';
-        return $result;
-    }
-    static function getInstance()
-    {
-        static $obj = null;
-        return $obj ? $obj : $obj = new self;
-    }
-}

HatenaSyntax/trunk/code/HatenaSyntax/SuperPre.php

@@ -1,45 +0,0 @@
-<?php
-
-// SuperPre := ">|" (alpha alnum*)? "|" SuperPreElement+ "||<" EndOfLine @doing
-class HatenaSyntax_SuperPre implements PEG_IParser
-{
-    protected $p;
-    function __construct()
-    {
-        $this->p = new PEG_Many1(new HatenaSyntax_SuperPreElement());
-    }
-    function parse(PEG_IContext $context)
-    {
-        PEG_Token::get('>|')->parse($context);
-        $ext = '';
-        for (;;) {
-            if ($context->eos()) throw new PEG_Failure;
-            $c = $context->read(1);
-            if (ctype_alnum($c)) {
-                $ext .= $c;
-            }
-            else {
-                $context->seek($context->tell() - 1);
-                break;
-            }
-        }
-        PEG_Token::get('|')->parse($context);
-
-        $result = $this->p->parse($context);
-
-        $nl = new PEG_Optional(HatenaSyntax_NewLine::getInstance());
-        $nl->parse($context);
-
-        PEG_Token::get('||<')->parse($context);
-        HatenaSyntax_EndOfLine::getInstance()->parse($context);
-
-        return array('type' => 'superpre',
-                     'ext' => $ext,
-                     'body' => $result);
-    }
-    static function getInstance()
-    {
-        static $o = null;
-        return $o ? $o : $o = new self;
-    }
-}

HatenaSyntax/trunk/code/HatenaSyntax/BlockQuote.php

@@ -1,33 +0,0 @@
-<?php
-
-// - BlockQuote := ">>" NewLine Element* "<<" EndOfLine
-class HatenaSyntax_BlockQuote implements PEG_IParser
-{
-    protected $p;
-    function setElement(HatenaSyntax_Element $elt)
-    {
-        $elt = new PEG_Sequence(array(new PEG_LookaheadNot(new PEG_Sequence(array(PEG_Token::get('<<'), HatenaSyntax_EndOfLine::getInstance()))), $elt));
-        $this->p = new PEG_Sequence();
-        $this->p->with(PEG_Token::get('>>'))
-                ->with(HatenaSyntax_NewLine::getInstance())
-                ->with(new PEG_Many($elt))
-                ->with(PEG_Token::get('<<'))
-                ->with(HatenaSyntax_EndOfLine::getInstance());
-    }
-    function parse(PEG_IContext $context)
-    {
-        $result = $this->p->parse($context);
-        $ret = array();
-        foreach ($result[2] as $elt) {
-            $ret[] = $elt[1];
-        }
-
-        return array('type' => 'blockquote',
-                     'body' => $ret);
-    }
-    static function getInstance()
-    {
-        static $o = null;
-        return $o ? $o : $o = new self;
-    }
-}
\ ファイルの末尾に改行がありません

HatenaSyntax/trunk/code/HatenaSyntax/LineSegment.php

@@ -1,25 +0,0 @@
-<?php
-
-class HatenaSyntax_LineSegment extends PEG_Action
-{
-    function __construct(PEG_IParser $p, $optional = false)
-    {
-        $elt = new HatenaSyntax_LineElement($p);
-        $parser = $optional ? new PEG_Many($elt) : new PEG_Many1($elt);
-        parent::__construct($parser);
-    }
-    function process($result)
-    {
-        $ret = array();
-        foreach ($result as $elt) {
-            if (!count($ret) || !is_string($elt) || !is_string(end($ret))) {
-                $ret[] = $elt;
-            }
-            elseif (count($ret) && is_string($elt) && is_string(end($ret))) {
-                $ret[count($ret) - 1] .= $elt;
-            }
-            else throw new Exception($elt);
-        }
-        return $ret;
-    }
-}

HatenaSyntax/trunk/code/HatenaSyntax/List.php

@@ -1,60 +0,0 @@
-<?php
-
-// - ListLine := ("+" | "-")+ Line<-(optional=true) EndOfLine
-// - List := ListLine+
-
-class HatenaSyntax_List extends PEG_Action
-{
-    function __construct()
-    {
-        $line = new PEG_Sequence;
-        $line->with(new PEG_Many1(new PEG_Choice(array(PEG_Token::get('-'),
-                                                       PEG_Token::get('+')))))
-             ->with(new HatenaSyntax_Line(true))
-             ->with(HatenaSyntax_EndOfLine::getInstance());
-        $line = new PEG_CallbackAction(array($this, 'processLine'), $line);
-
-        parent::__construct(new PEG_Many1($line));
-    }
-    function process($result)
-    {
-        return $this->make($result);
-    }
-    protected function make(Array &$arr, $level = 0)
-    {
-        $body = array();
-
-        if ($elt = current($arr)) {
-            $head = $elt['type'];
-        }
-        else {
-            $head = '';
-        }
-
-        while ($elt = current($arr)) {
-            if ($elt['level'] === $level) {
-                $body[] = $elt['body'];
-                next($arr);
-            }
-            elseif ($elt['level'] > $level) {
-                $body[] = $this->make($arr, $level + 1);
-            }
-            else {
-                break;
-            }
-        }
-
-        return array('type' => 'list',
-                     'head' => $head,
-                     'body' => $body);
-    }
-    function processLine($result)
-    {
-        $type = end($result[0]); // '+' or '-'
-        $level = count($result[0]) - 1;
-        $body = $result[1];
-        return array('type' => $type,
-                     'level' => $level,
-                     'body' => $body);
-    }
-}
\ ファイルの末尾に改行がありません

HatenaSyntax/trunk/code/HatenaSyntax/SuperPreElement.php

@@ -1,28 +0,0 @@
-<?php
-
-// SuperPreElement := NewLine (?! "||<" EndOfLine) ((?! "||<" EndOfLine) LineChar)*
-class HatenaSyntax_SuperPreElement extends PEG_Action
-{
-    function __construct()
-    {
-        $cond = new PEG_LookaheadNot(new PEG_Sequence(array(PEG_Token::get('||<',
-                                                            HatenaSyntax_EndOfLine::getInstance()))));
-        $elt = new PEG_Sequence();
-        $elt->with($cond)
-            ->with(HatenaSyntax_LineChar::getInstance());
-
-        $parser = new PEG_Sequence();
-        $parser->with(HatenaSyntax_NewLine::getInstance())
-               ->with($cond)
-               ->with(new PEG_Many($elt));
-        parent::__construct($parser);
-    }
-    function process($result)
-    {
-        $line = '';
-        foreach ($result[2] as $elt) {
-            $line .= $elt[1];
-        }
-        return $line;
-    }
-}
\ ファイルの末尾に改行がありません

HatenaSyntax/trunk/code/HatenaSyntax/DefinitionList.php

@@ -1,14 +0,0 @@
-<?php
-
-class HatenaSyntax_DefinitionList extends PEG_Action
-{
-    function __construct()
-    {
-        parent::__construct(new PEG_Many1(new HatenaSyntax_Definition));
-    }
-    function process($result)
-    {
-        return array('type' => 'definitionlist',
-                     'body' => $result);
-    }
-}
\ ファイルの末尾に改行がありません

HatenaSyntax/trunk/code/HatenaSyntax/LineChar.php

@@ -1,22 +0,0 @@
-<?php
-
-class HatenaSyntax_LineChar extends PEG_Action
-{
-    function __construct()
-    {
-        $nl = new PEG_Choice();
-        $nl->with(PEG_Token::get("\n"))->with(PEG_Token::get("\r"));
-        $lookahead = new PEG_Lookahead(new PEG_Not($nl));
-        $linechar = new PEG_Sequence(array($lookahead, PEG_Anything::getInstance()));
-        parent::__construct($linechar);
-    }
-    function process($result)
-    {
-        return $result[1];
-    }
-    static function getInstance()
-    {
-        static $obj = null;
-        return $obj ? $obj : $obj = new self;
-    }
-}
\ ファイルの末尾に改行がありません

HatenaSyntax/trunk/code/HatenaSyntax/Pre.php

@@ -1,42 +0,0 @@
-<?php
-
-// Pre := ">|" (NewLine LineSegment<-( "|<" EndOfLine, true))+ "|<" EndOfLine
-class HatenaSyntax_Pre extends PEG_Action
-{
-    function __construct()
-    {
-        $newline = HatenaSyntax_NewLine::getInstance();
-
-        $eol = HatenaSyntax_EndOfLine::getInstance();
-
-        $cond = new PEG_Sequence();
-        $cond->with(PEG_Token::get('|<'))
-             ->with($eol);
-
-        $line = new PEG_Sequence();
-        $line->with($newline)
-             ->with(new HatenaSyntax_LineSegment($cond, true));
-
-        $parser = new PEG_Sequence();
-        $parser->with(PEG_Token::get('>|'))
-               ->with(new PEG_Many1($line))
-               ->with(PEG_Token::get('|<'))
-               ->with($eol);
-        parent::__construct($parser);
-    }
-    function process($result)
-    {
-        $ret = array();
-        $arr = $result[1];
-        foreach ($arr as $elt) if (count($elt[1])) {
-            $ret[] = $elt[1];
-        }
-        return array('type' => 'pre',
-                     'body' => $ret);
-    }
-    static function getInstance()
-    {
-        static $o = null;
-        return $o ? $o : $o = new self;
-    }
-}
\ ファイルの末尾に改行がありません

HatenaSyntax/trunk/code/HatenaSyntax/Line.php

@@ -1,26 +0,0 @@
-<?php
-
-class HatenaSyntax_Line extends PEG_Action
-{
-    function __construct($optional = false)
-    {
-        $elt = new HatenaSyntax_LineElement;
-        $parser = $optional ? new PEG_Many($elt) : new PEG_Many1($elt);
-        parent::__construct($parser);
-    }
-    function process($result)
-    {
-
-        $ret = array();
-        foreach ($result as $elt) {
-            if (!count($ret) || !is_string($elt) || !is_string(end($ret))) {
-                $ret[] = $elt;
-            }
-            elseif (count($ret) && is_string($elt) && is_string(end($ret))) {
-                $ret[count($ret) - 1] .= $elt;
-            }
-            else throw new Exception($elt);
-        }
-        return $ret;
-    }
-}
\ ファイルの末尾に改行がありません

HatenaSyntax/trunk/code/HatenaSyntax/Table.php

@@ -1,32 +0,0 @@
-<?php
-
-// - TableLine := TableCell+ "|" EndOfLine
-// - Table := TableLine+
-class HatenaSyntax_Table extends PEG_Action
-{
-    function __construct()
-    {
-        $line = new PEG_Sequence();
-        $line->with(new PEG_Many1(new HatenaSyntax_TableCell()))
-             ->with(PEG_Token::get('|'))
-             ->with(HatenaSyntax_EndOfLine::getInstance());
-        $parser = new PEG_Many1($line);
-
-        parent::__construct($parser);
-    }
-    function process($result)
-    {
-        $ret = array();
-        foreach ($result as $elt) {
-            $ret[] = $elt[0];
-        }
-
-        return array('type' => 'table',
-                     'body' => $ret);
-    }
-    static function getInstance()
-    {
-        static $o = null;
-        return $o ? $o : $o = new self;
-    }
-}
\ ファイルの末尾に改行がありません

HatenaSyntax/trunk/code/HatenaSyntax/TableCell.php

@@ -1,20 +0,0 @@
-<?php
-
-// - TableCell := "|" "*"? LineSegment<-("|", optional=true)
-class HatenaSyntax_TableCell extends PEG_Action
-{
-    function __construct()
-    {
-        $parser = new PEG_Sequence();
-        $parser->with(PEG_Token::get('|'))
-               ->with(new PEG_LookaheadNot(HatenaSyntax_EndOfLine::getInstance()))
-               ->with(new PEG_Optional(PEG_Token::get('*')))
-               ->with(new HatenaSyntax_LineSegment(PEG_Token::get('|'), true));
-        parent::__construct($parser);
-    }
-    function process($result)
-    {
-        return array('header' => !!$result[2],
-                     'body' => $result[3]);
-    }
-}
\ ファイルの末尾に改行がありません

HatenaSyntax/trunk/code/HatenaSyntax/Header.php

@@ -1,30 +0,0 @@
-<?php
-
-class HatenaSyntax_Header extends PEG_Action
-{
-    function __construct()
-    {
-        $lc = HatenaSyntax_LineChar::getInstance();
-        $fn = HatenaSyntax_Footnote::getInstance();
-        $parser = new PEG_Sequence();
-        $parser->with(new PEG_Many1(PEG_Token::get('*')))
-               ->with(new PEG_Many(new PEG_Choice(array($fn, $lc))))
-               ->with(HatenaSyntax_EndOfLine::getInstance());
-        parent::__construct($parser);
-    }
-    protected function process($result)
-    {
-        $body = array();
-        foreach ($result[1] as $elt) {
-            if (count($body) && is_string($elt) && is_string(end($body))) {
-                $body[count($body) - 1] .= $elt;
-            }
-            else {
-                $body[] = $elt;
-            }
-        }
-        return array('type' => 'header',
-                     'level' => count($result[0]),
-                     'body' => $body);
-    }
-}
\ ファイルの末尾に改行がありません

HatenaSyntax/trunk/code/HatenaSyntax/Definition.php

@@ -1,22 +0,0 @@
-<?php
-
-class HatenaSyntax_Definition extends PEG_Action
-{
-    function __construct()
-    {
-        $eol = HatenaSyntax_EndOfLine::getInstance();
-
-        $content = new HatenaSyntax_LineSegment(PEG_Token::get(':'));
-        $parser = new PEG_Sequence();
-        $parser->with(PEG_Token::get(':'))
-               ->with($content)
-               ->with(PEG_Token::get(':'))
-               ->with($content)
-               ->with($eol);
-        parent::__construct($parser);
-    }
-    function process($result)
-    {
-        return array($result[1], $result[3]);
-    }
-}
\ ファイルの末尾に改行がありません

HatenaSyntax/trunk/code/HatenaSyntax/NodeCreater.php

@@ -0,0 +1,27 @@
+<?php
+
+class HatenaSyntax_NodeCreater implements PEG_IParser
+{
+    protected $keys, $parser;
+    function __construct($type, PEG_IParser $parser, Array $keys = array())
+    {
+        $this->type = $type;
+        $this->keys = $keys;
+        $this->parser = $parser;
+    }
+    function parse(PEG_IContext $context)
+    {
+        $result = $this->parser->parse($context);
+        if ($result instanceof PEG_Failure) return $result;
+
+        $data = array();
+        if (count($this->keys) > 0) foreach ($this->keys as $i => $key) {
+            $data[$key] = $result[$i];
+        }
+        else {
+            $data = $result;
+        }
+
+        return new HatenaSyntax_Node($this->type, $data);
+    }
+}
\ ファイルの末尾に改行がありません

HatenaSyntax/trunk/code/HatenaSyntax/Util.php

@@ -0,0 +1,67 @@
+<?php
+
+class HatenaSyntax_Util
+{
+    static function normalizeList(Array $data)
+    {
+        return self::filterLevel(self::lower(self::levels($data), $data));
+    }
+
+    static function segment(PEG_IParser $p)
+    {
+        return PEG::callbackAction(array('HatenaSyntax_Util', 'normalizeLineSegment'), $p);
+    }
+
+    static function normalizeLineSegment(Array $data)
+    {
+        for ($ret = array(), $i = 0, $len = count($data); $i < $len; $i++) {
+            if (is_string($data[$i])) {
+                for ($str = $data[$i++]; $i < $len && is_string($data[$i]); $i++) {
+                    $str .= $data[$i];
+                }
+                $ret[] = $str;
+                if ($i < $len) $ret[] = $data[$i];
+            }
+            else {
+                $ret[] = $data[$i];
+            }
+        }
+        return $ret;
+    }
+
+    static protected function filterLevel($struct)
+    {
+        foreach ($struct as &$node)
+            $node = is_string($node[0]) ? array($node[0], $node[2]) : self::filterLevel($node);
+        return $struct;
+    }
+
+    static protected function lower(Array $levels, Array $data)
+    {
+        $level = array_pop($levels);
+
+        for ($i = 0, $ret = array(), $len = count($data); $i < $len; $i++) {
+            if ($data[$i][1] <= $level) {
+                $ret[] = $data[$i];
+            }
+            else {
+                for ($arr = array($data[$i++]); $i < $len && $data[$i][1] > $level; $i++) {
+                    $arr[] = $data[$i];
+                }
+                $ret[] = self::lower($levels, $arr);
+                if ($i < $len) $ret[] = $data[$i];
+            }
+        }
+
+        return $ret;
+    }
+
+    static protected function levels(Array $data)
+    {
+        $levels = array();
+        foreach ($data as $li) $levels[$li[1]] = true;
+        $levels = array_keys($levels);
+        rsort($levels, SORT_NUMERIC);
+        return $levels;
+    }
+}
\ ファイルの末尾に改行がありません

HatenaSyntax/trunk/code/HatenaSyntax/Locator.php

@@ -0,0 +1,232 @@
+<?php
+
+class HatenaSyntax_Locator
+{
+    protected $objects = array();
+
+    private function __construct()
+    {
+        $this->setup();
+    }
+
+    static function it()
+    {
+        static $obj = null;
+        return $obj ? $obj : $obj = new self;
+    }
+
+    function __get($name)
+    {
+        return isset($this->objects[$name]) ?
+            $this->objects[$name] :
+            $this->objects[$name] = $this->{'create' . $name}();
+    }
+
+    protected function createFactory()
+    {
+        return new HatenaSyntax_Factory($this);
+    }
+
+    protected function createLineChar()
+    {
+        return PEG::not(PEG::char("\n\r"));
+    }
+
+    protected function createEndOfLine()
+    {
+        return PEG::choice(PEG::newLine(), PEG::eos());
+    }
+
+    protected function createFootnote()
+    {
+        $close = '))';
+        $elt = PEG::andalso(PEG::not($close),
+                            PEG::choice($this->link, $this->lineChar));
+
+        $parser = PEG::pack('((',
+                            HatenaSyntax_Util::segment(PEG::many1($elt)),
+                            $close);
+
+        return $this->factory->createNodeCreater('footnote', $parser);
+    }
+
+    protected function createLineElement()
+    {
+        return $this->factory->createLineElement();
+    }
+
+    protected function createLineSegment()
+    {
+        return HatenaSyntax_Util::segment(PEG::many($this->lineElement));
+    }
+
+    protected function createHttpLink()
+    {
+        $title_char = PEG::andalso(PEG::not(']'),
+                                   $this->lineChar);
+
+        $title = PEG::secondSeq(':title=', PEG::join(PEG::many1($title_char)));
+
+        $url_char = PEG::andalso(PEG::not(PEG::choice(']', ':title=')),
+                                 $this->lineChar);
+
+        $url = PEG::join(PEG::seq(PEG::choice('http://', 'https://'),
+                                  PEG::many1($url_char)));
+        $parser = PEG::seq($url, PEG::optional($title));
+
+        return $this->factory->createNodeCreater('httplink', $parser, array('href', 'title'));
+    }
+
+    protected function createLink()
+    {
+        return PEG::pack('[', PEG::choice($this->httpLink), ']');
+    }
+
+    protected function createDefinition()
+    {
+        $c = PEG::token(':');
+        $sep = PEG::drop($c);
+        $factory = $this->factory;
+        $parser = PEG::seq($sep,
+                           $factory->createLineSegment($c, true),
+                           $sep,
+                           $factory->createLineSegment($c),
+                           PEG::drop($this->endOfLine));
+        return $parser;
+    }
+
+    protected function createDefinitionList()
+    {
+        $parser = PEG::many1($this->definition);
+        return $this->factory->createNodeCreater('definitionlist', $parser);
+    }
+
+    protected function createPre()
+    {
+        $nl = PEG::newLine();
+        $closing = PEG::seq(PEG::optional($nl), '|<', $this->endOfLine);
+        $line = PEG::secondSeq($nl, $this->factory->createLineSegment($closing));
+        $parser = PEG::pack('>|', PEG::many1($line), $closing);
+
+        return $this->factory->createNodeCreater('pre', $parser);
+    }
+
+    protected function createSuperPreElement()
+    {
+        $cond = PEG::lookaheadNot(PEG::seq('||<', $this->endOfLine));
+        $elt = PEG::secondSeq($cond, $this->lineChar);
+        $parser = PEG::thirdSeq(PEG::newLine(), $cond, PEG::join(PEG::many($elt)));
+
+        return $parser;
+
+    }
+
+    protected function createHeader()
+    {
+        $parser = PEG::seq(PEG::drop('*'),
+                           PEG::count(PEG::many('*')),
+                           HatenaSyntax_Util::segment(PEG::many(PEG::choice($this->lineChar, $this->footnote))),
+                           PEG::drop($this->endOfLine));
+
+        return $this->factory->createNodeCreater('header', $parser, array('level', 'body'));
+    }
+
+    protected function createSuperPre()
+    {
+        $open = PEG::pack('>|',
+                          PEG::join(PEG::many(PEG::not(PEG::char("\r\n|")))),
+                          '|');
+        $body = PEG::many1($this->superPreElement);
+
+        $close = PEG::drop(PEG::optional(PEG::newLine()),
+                           '||<',
+                           $this->endOfLine);
+
+        $parser = PEG::seq($open, $body, $close);
+
+        return $this->factory->createNodeCreater('superpre', $parser, array('type', 'body'));
+    }
+
+    protected function createList()
+    {
+        $c = PEG::char('-+');
+        $item = PEG::seq($c,
+                         PEG::count(PEG::many($c)),
+                         $this->lineSegment,
+                         PEG::drop($this->endOfLine));
+        $list = PEG::callbackAction(array('HatenaSyntax_Util', 'normalizeList'), PEG::many1($item));
+
+        return $this->factory->createNodeCreater('list', $list);
+    }
+
+    protected function createTableCell()
+    {
+        $parser = PEG::seq(PEG::drop('|', PEG::lookaheadNot($this->endOfLine)),
+                           PEG::optional('*'),
+                           $this->factory->createLineSegment(PEG::token('|'), true));
+        return $parser;
+    }
+
+    protected function createTable()
+    {
+        $line = PEG::firstSeq(PEG::many1($this->tableCell),
+                              '|',
+                              $this->endOfLine);
+        $parser = PEG::many1($line);
+
+        return $this->factory->createNodeCreater('table', $parser);
+    }
+
+    protected function createBlockQuote()
+    {
+        $elt = PEG::secondSeq(PEG::lookaheadNot(PEG::seq('<<', $this->endOfLine)),
+                              $this->element);
+
+        $parser = PEG::thirdSeq('>>',
+                                PEG::newLine(),
+                                PEG::many1($elt),
+                                '<<',
+                                $this->endOfLine);
+
+        return $this->factory->createNodeCreater('blockquote', $parser);
+    }
+
+    protected function createParagraph()
+    {
+        $parser = PEG::firstSeq($this->lineSegment, $this->endOfLine);
+
+        return $this->factory->createNodeCreater('paragraph', $parser);
+    }
+
+    protected function createEmptyParagraph()
+    {
+        $parser = PEG::count(PEG::many1(PEG::newLine()));
+        return $this->factory->createNodeCreater('emptyparagraph', $parser);
+    }
+
+    protected function createElement()
+    {
+        $parser = PEG::ref();
+        return $parser;
+    }
+
+    protected function createParser()
+    {
+        return $this->factory->createNodeCreater('root', PEG::many($this->element));
+    }
+
+    protected function setup()
+    {
+        $this->element->is(PEG::choice($this->header,
+                                        $this->blockQuote,
+                                        $this->definitionList,
+                                        $this->table,
+                                        $this->list,
+                                        $this->pre,
+                                        $this->superpre,
+                                        $this->emptyParagraph,
+                                        $this->paragraph));
+    }
+
+
+}
\ ファイルの末尾に改行がありません

HatenaSyntax/trunk/code/HatenaSyntax/Node.php

@@ -0,0 +1,21 @@
+<?php
+
+class HatenaSyntax_Node
+{
+    protected $type, $data = array();
+    function __construct($type, $data)
+    {
+        $this->type = $type;
+        $this->data = $data;
+    }
+
+    function getType()
+    {
+        return $this->type;
+    }
+
+    function getData()
+    {
+        return $this->data;
+    }
+}
\ ファイルの末尾に改行がありません

HatenaSyntax/trunk/code/HatenaSyntax/Renderer.php

@@ -0,0 +1,207 @@
+<?php
+
+class HatenaSyntax_Renderer
+{
+    protected $config, $footnote, $fncount, $padding, $encoding;
+
+    function __construct(Array $config = array())
+    {
+        $this->config = $config + array(
+            'headerlevel' => 1,
+            'htmlescape' => true,
+            'id' => uniqid('sec'),
+            'sectionclass' => 'section',
+            'footnoteclass' => 'footnote',
+            'superprehandler' => array($this, 'superPreHandler')
+        );
+    }
+
+    function render(HatenaSyntax_Node $node)
+    {
+        $this->footnote = '';
+        $this->fncount = 0;
+        $this->padding = 0;
+
+        $ret = $this->renderNode($node);
+        $ret = '<div class="' . $this->config['sectionclass'] . '">' . PHP_EOL . $ret . PHP_EOL . '</div>' . PHP_EOL;
+        if ($this->fncount > 0) {
+            $ret .= PHP_EOL . PHP_EOL . '<div class="' . $this->config['footnoteclass'] . '">' .
+                    PHP_EOL . $this->footnote .  '</div>';
+        }
+
+        return $ret;
+    }
+
+    static function superPreHandler($type, $lines)
+    {
+        return join(PHP_EOL, array_map(array('HatenaSyntax_Renderer', 'escape'), $lines));
+    }
+
+    protected function renderNode(HatenaSyntax_Node $node)
+    {
+        $this->padding++;
+        $ret = $this->{'render' . $node->getType()}($node->getData());
+        $this->padding--;
+        return $ret;
+    }
+
+    protected function renderRoot(Array $arr)
+    {
+        $this->padding--;
+        foreach ($arr as &$elt) $elt = $this->renderNode($elt);
+        $this->padding++;
+        return join(PHP_EOL, $arr);
+    }
+
+    protected function renderHeader(Array $data)
+    {
+        $level = $data['level'] + $this->config['headerlevel'];
+        return $this->line("<h{$level}>" . $this->renderLineSegment($data['body']) . "</h{$level}>");
+    }
+
+    protected function renderLineSegment(Array $data)
+    {
+        foreach ($data as &$elt)
+            $elt = is_string($elt) ? ($this->config['htmlescape'] ? $this->escape($elt) : $elt)
+                                   : $this->renderNode($elt);
+        return join('', $data);
+    }
+
+    protected function renderFootnote(Array $data)
+    {
+        $this->fncount++;
+        $id = $this->config['id'];
+        $n = $this->fncount;
+        $title = $body = $this->renderLineSegment($data);
+        $title = strip_tags($body);
+        if (!$this->config['htmlescape']) {
+            $title = $this->escape($title);
+        }
+
+        $this->footnote .= sprintf('  <p><a href="#%s_%d" name="%s_footntoe_%d">*%d</a>: %s</p>' . PHP_EOL, $id, $n, $id , $n, $n, $body);
+        return sprintf('(<a href="#%s_footnote_%d" name="%s_%d" title="%s">*%d</a>)', $id, $n, $id, $n, $title, $n);
+    }
+
+    protected function renderHttpLink(Array $data)
+    {
+        list($href, $title) = array($data['href'], $data['title']);
+        $title = $title ? $title : $href;
+        if ($this->config['htmlescape']) $title = $this->escape($title);
+        $href = $this->escape($href);
+        return sprintf('<a href="%s">%s</a>', $href, $title);
+    }
+
+    protected function renderDefinitionList(Array $data)
+    {
+        foreach ($data as &$elt) $elt = $this->renderDefinition($elt);
+        return join(PHP_EOL, array($this->line('<dl>'), join(PHP_EOL, $data), $this->line('</dl>')));
+    }
+
+    protected function renderDefinition(Array $data)
+    {
+        list($dt, $dd) = $data;
+        $ret = array();
+        if ($dt) $ret[] = $this->line('<dt>' . $this->renderLineSegment($dt) . '</dt>', 1);
+        $ret[] = $this->line('<dd>' . $this->renderLineSegment($dd) . '</dd>', 1);
+        return join(PHP_EOL, $ret);
+    }
+
+    protected function renderPre(Array $data)
+    {
+        $ret = array();
+        $ret[] = $this->line('<pre>');
+        foreach ($data as &$elt) $elt = $this->renderLineSegment($elt);
+        $ret[] = join(PHP_EOL, $data);
+        $ret[] = $this->line('</pre>');
+        return join(PHP_EOL, $ret);
+    }
+
+    protected function renderSuperPre(Array $data)
+    {
+        $ret = array();
+        list($type, $lines) = array($data['type'], $data['body']);
+        $ret[] = $this->line('<pre class="superpre ' . $type . '">');
+        $ret[] = call_user_func($this->config['superprehandler'], $type, $lines);
+        $ret[] = $this->line('</pre>');
+        return join(PHP_EOL, $ret);
+    }
+
+    protected function renderTable(Array $data)
+    {
+        $ret = array();
+        $ret[] = $this->line('<table>');
+        $this->padding++;
+        foreach ($data as $tr) {
+            $ret[] = $this->line('<tr>');
+            foreach ($tr as $td) $ret[] = $this->renderTableCell($td[0], $td[1]);
+            $ret[] = $this->line('</tr>');
+        }
+        $this->padding--;
+        $ret[] = $this->line('</table>');
+        return join(PHP_EOL, $ret);
+    }
+
+    protected function renderTableCell($header, $segment)
+    {
+        $tag = $header ? 'th' : 'td';
+        $ret = $this->line("<{$tag}>" . $this->renderLineSegment($segment) . "</{$tag}>", 1);
+        return $ret;
+    }
+
+    protected function renderBlockQuote(Array $arr)
+    {
+        $ret = array();
+        $ret[] = $this->line('<blockquote>');
+        foreach ($arr as $elt) $ret[] = $this->renderNode($elt);
+        $ret[] = $this->line('</blockquote>');
+        return join(PHP_EOL, $ret);
+    }
+
+    protected function renderParagraph(Array $data)
+    {
+        return $this->line('<p>' . $this->renderLineSegment($data) . '</p>');
+    }
+
+    protected function renderEmptyParagraph($data)
+    {
+        $ret = array();
+        for ($data--; $data > 0; $data--) $ret[] = $this->line('<br>');
+        return join(PHP_EOL, $ret);
+    }
+
+    protected function renderList(Array $data)
+    {
+        $this->padding--;
+        $ret = $this->renderListItem($data);
+        $this->padding++;
+        return $ret;
+    }
+
+    protected function renderListItem(Array $data)
+    {
+        $this->padding++;
+        if (is_string($data[0])) { // leaf case
+            $result = $this->line('<li>' . $this->renderLineSegment($data[1]) . '</li>');
+        }
+        else {
+            $buf = array();
+            $name = $data[0][0] === '+' ? 'ol' : 'ul';
+            $buf[] = $this->line("<{$name}>");
+            foreach ($data as $elt) $buf[] = $this->renderListItem($elt);
+            $buf[] = $this->line("</{$name}>");
+            $result = join(PHP_EOL, $buf);
+        }
+        $this->padding--;
+        return $result;
+    }
+
+    protected function line($str = '', $padding = 0)
+    {
+        return str_repeat('  ', max($this->padding + $padding, 0)) . $str;
+    }
+
+    protected static function escape($str)
+    {
+        return htmlspecialchars($str, ENT_QUOTES);
+    }
+}
\ ファイルの末尾に改行がありません

HatenaSyntax/trunk/code/HatenaSyntax/Factory.php

@@ -0,0 +1,38 @@
+<?php
+
+class HatenaSyntax_Factory
+{
+    protected $locator;
+
+    function __construct(HatenaSyntax_Locator $locator)
+    {
+        $this->locator = $locator;
+    }
+
+    protected function __get($name)
+    {
+        return strtolower($name) === 'locator' ? $this->locator : $this->locator->$name;
+    }
+
+    function createLineElement(PEG_IParser $cond_parser = null)
+    {
+        $locator = $this->locator;
+
+        $item = PEG::choice($locator->link, $locator->footnote, $locator->lineChar);
+        $parser = is_null($cond_parser) ? $item : PEG::second(PEG::seq(PEG::lookaheadNot($cond_parser), $item));
+
+        return $parser;
+    }
+
+    function createLineSegment(PEG_IParser $cond_parser, $optional = false)
+    {
+        $elt = $this->createLineElement($cond_parser);
+        $parser = $optional ? PEG::many($elt) : PEG::many1($elt);
+        return HatenaSyntax_Util::segment($parser);
+    }
+
+    function createNodeCreater($type, PEG_IParser $parser, Array $keys = array())
+    {
+        return new HatenaSyntax_NodeCreater($type, $parser, $keys);
+    }
+}
\ ファイルの末尾に改行がありません

HatenaSyntax/trunk/code/HatenaSyntax.php

@@ -1,23 +1,23 @@
<?php
-include_once 'PEG.php';
-include_once dirname(__FILE__) . '/HatenaSyntax/BlockQuote.php';
-include_once dirname(__FILE__) . '/HatenaSyntax/Definition.php';
-include_once dirname(__FILE__) . '/HatenaSyntax/DefinitionList.php';
-include_once dirname(__FILE__) . '/HatenaSyntax/Element.php';
-include_once dirname(__FILE__) . '/HatenaSyntax/EndOfLine.php';
-include_once dirname(__FILE__) . '/HatenaSyntax/Footnote.php';
-include_once dirname(__FILE__) . '/HatenaSyntax/Header.php';
-include_once dirname(__FILE__) . '/HatenaSyntax/HttpLink.php';
-include_once dirname(__FILE__) . '/HatenaSyntax/Line.php';
-include_once dirname(__FILE__) . '/HatenaSyntax/LineChar.php';
-include_once dirname(__FILE__) . '/HatenaSyntax/LineElement.php';
-include_once dirname(__FILE__) . '/HatenaSyntax/LineSegment.php';
-include_once dirname(__FILE__) . '/HatenaSyntax/Link.php';
-include_once dirname(__FILE__) . '/HatenaSyntax/List.php';
-include_once dirname(__FILE__) . '/HatenaSyntax/NewLine.php';
-include_once dirname(__FILE__) . '/HatenaSyntax/Parser.php';
-include_once dirname(__FILE__) . '/HatenaSyntax/Pre.php';
-include_once dirname(__FILE__) . '/HatenaSyntax/SuperPre.php';
-include_once dirname(__FILE__) . '/HatenaSyntax/SuperPreElement.php';
-include_once dirname(__FILE__) . '/HatenaSyntax/Table.php';
-include_once dirname(__FILE__) . '/HatenaSyntax/TableCell.php';
\ ファイルの末尾に改行がありません
+include_once 'PEG.php';
+include_once dirname(__FILE__) . '/HatenaSyntax/Node.php';
+include_once dirname(__FILE__) . '/HatenaSyntax/Locator.php';
+include_once dirname(__FILE__) . '/HatenaSyntax/Factory.php';
+include_once dirname(__FILE__) . '/HatenaSyntax/NodeCreater.php';
+include_once dirname(__FILE__) . '/HatenaSyntax/Renderer.php';
+include_once dirname(__FILE__) . '/HatenaSyntax/Util.php';
+
+class HatenaSyntax
+{
+    static function parse($str)
+    {
+        return HatenaSyntax_Locator::it()->parser->parse(PEG::context($str));
+    }
+
+    static function render($str, $config = array())
+    {
+        $node = self::parse($str);
+        $renderer = new HatenaSyntax_Renderer($config);
+        return $renderer->render($node);
+    }
+}
\ ファイルの末尾に改行がありません