-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Start work on creating a new document parser.
The DocumentParser class takes a Markdown-formatted string and turns it into a tree of objects, as described in the CommonMark spec. Refs #1.
- Loading branch information
1 parent
27f0d88
commit ada2106
Showing
12 changed files
with
473 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,99 @@ | ||
<?php | ||
|
||
namespace FluxBB\Markdown; | ||
|
||
use FluxBB\Markdown\Node\Document; | ||
use FluxBB\Markdown\Node\NodeAcceptorInterface; | ||
use FluxBB\Markdown\Parser\BlockquoteParser; | ||
use FluxBB\Markdown\Parser\ParagraphParser; | ||
use FluxBB\Markdown\Parser\ParserInterface; | ||
|
||
class DocumentParser | ||
{ | ||
|
||
/** | ||
* @var ParserInterface[] | ||
*/ | ||
protected $parsers = []; | ||
|
||
|
||
/** | ||
* Create a parser instance. | ||
*/ | ||
public function __construct() | ||
{ | ||
$this->registerDefaultParsers(); | ||
} | ||
|
||
/** | ||
* Parse the given Markdown text into a document tree. | ||
* | ||
* @param string $markdown | ||
* @return Document | ||
*/ | ||
public function parse($markdown) | ||
{ | ||
$target = $root = new Document(); | ||
$parser = $this->buildParserStack(); | ||
|
||
$lines = explode("\n", $markdown); | ||
foreach ($lines as $line) { | ||
$target = call_user_func($parser, $line, $target); | ||
} | ||
|
||
return $root; | ||
} | ||
|
||
/** | ||
* Register all standard parsers. | ||
* | ||
* @return void | ||
*/ | ||
protected function registerDefaultParsers() | ||
{ | ||
$this->parsers = [ | ||
new BlockquoteParser(), | ||
new ParagraphParser(), | ||
]; | ||
} | ||
|
||
/** | ||
* Build the nested stack of closures that executes the parsers in the correct order. | ||
* | ||
* @return callable | ||
*/ | ||
protected function buildParserStack() | ||
{ | ||
$parsers = array_reverse($this->parsers); | ||
$initial = $this->getInitialClosure(); | ||
|
||
return array_reduce($parsers, $this->getParserClosure(), $initial); | ||
} | ||
|
||
/** | ||
* Create the closure that returns another closure to be passed to each parser. | ||
* | ||
* @return callable | ||
*/ | ||
protected function getParserClosure() | ||
{ | ||
return function ($stack, ParserInterface $parser) { | ||
return function ($line, NodeAcceptorInterface $target) use ($stack, $parser) { | ||
return $parser->parseLine($line, $target, $stack); | ||
}; | ||
}; | ||
} | ||
|
||
/** | ||
* Create the fallback closure that simply returns the target node and throws away any content. | ||
* | ||
* @return callable | ||
*/ | ||
protected function getInitialClosure() | ||
{ | ||
return function ($line, NodeAcceptorInterface $target) { | ||
return $target; | ||
}; | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
<?php | ||
|
||
namespace FluxBB\Markdown\Node; | ||
|
||
abstract class Block extends Node implements NodeAcceptorInterface | ||
{ | ||
|
||
protected $open = true; | ||
|
||
|
||
abstract public function canContain(Node $other); | ||
|
||
public function toString() | ||
{ | ||
return ($this->isOpen() ? '-> ' : '') . parent::toString(); | ||
} | ||
|
||
public function push(Node $child) | ||
{ | ||
if ($this->isOpen()) { | ||
if ($this->canContain($child)) { | ||
$this->addChild($child); | ||
return; | ||
} else { | ||
$this->close(); | ||
} | ||
} else { | ||
$this->getParent()->push($child); | ||
} | ||
} | ||
|
||
public function isOpen() | ||
{ | ||
return $this->open; | ||
} | ||
|
||
public function close() | ||
{ | ||
$this->open = false; | ||
} | ||
|
||
/* | ||
* Block acceptor methods | ||
*/ | ||
|
||
public function accept(NodeInterface $node) | ||
{ | ||
return $node->proposeTo($this); | ||
} | ||
|
||
public function acceptParagraph(Paragraph $paragraph) | ||
{ | ||
return $this->getParent()->acceptParagraph($paragraph); | ||
} | ||
|
||
public function acceptBlockquote(Blockquote $blockquote) | ||
{ | ||
return $this->getParent()->acceptBlockquote($blockquote); | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
<?php | ||
|
||
namespace FluxBB\Markdown\Node; | ||
|
||
class Blockquote extends Block implements NodeInterface, NodeAcceptorInterface | ||
{ | ||
|
||
public function getType() | ||
{ | ||
return 'block_quote'; | ||
} | ||
|
||
public function canContain(Node $other) | ||
{ | ||
return $other->getType() == 'paragraph'; | ||
} | ||
|
||
public function accepts(Node $block) | ||
{ | ||
return $block->getType() == 'paragraph'; | ||
} | ||
|
||
public function proposeTo(NodeAcceptorInterface $block) | ||
{ | ||
return $block->acceptBlockquote($this); | ||
} | ||
|
||
public function acceptParagraph(Paragraph $paragraph) | ||
{ | ||
$this->addChild($paragraph); | ||
|
||
return $paragraph; | ||
} | ||
|
||
public function acceptBlockquote(Blockquote $blockquote) | ||
{ | ||
$this->merge($blockquote); | ||
|
||
return $this; | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
<?php | ||
|
||
namespace FluxBB\Markdown\Node; | ||
|
||
class Document extends Block | ||
{ | ||
|
||
public function canContain(Node $other) | ||
{ | ||
return true; | ||
} | ||
|
||
public function isOpen() | ||
{ | ||
return true; | ||
} | ||
|
||
public function getType() | ||
{ | ||
return 'document'; | ||
} | ||
|
||
public function acceptParagraph(Paragraph $paragraph) | ||
{ | ||
$this->addChild($paragraph); | ||
|
||
return $paragraph; | ||
} | ||
|
||
public function acceptBlockquote(Blockquote $blockquote) | ||
{ | ||
$this->addChild($blockquote); | ||
|
||
return $blockquote; | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
<?php | ||
|
||
namespace FluxBB\Markdown\Node; | ||
|
||
abstract class LeafBlock extends Node implements NodeAcceptorInterface | ||
{ | ||
|
||
public function canContain(Node $other) | ||
{ | ||
return false; | ||
} | ||
|
||
public function accept(NodeInterface $node) | ||
{ | ||
return $node->proposeTo($this); | ||
} | ||
|
||
public function acceptParagraph(Paragraph $paragraph) | ||
{ | ||
return $this->parent->acceptParagraph($paragraph); | ||
} | ||
|
||
public function acceptBlockquote(Blockquote $blockquote) | ||
{ | ||
return $this->parent->acceptBlockquote($blockquote); | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
<?php | ||
|
||
namespace FluxBB\Markdown\Node; | ||
|
||
abstract class Node | ||
{ | ||
|
||
protected $children = []; | ||
|
||
/** | ||
* @var Node | ||
*/ | ||
protected $parent = null; | ||
|
||
|
||
abstract public function getType(); | ||
|
||
public function toString() | ||
{ | ||
return $this->getType(); | ||
} | ||
|
||
public function setParent(Node $parent) | ||
{ | ||
$this->parent = $parent; | ||
} | ||
|
||
public function getChildNodes() | ||
{ | ||
return $this->children; | ||
} | ||
|
||
public function addChild(Node $child) | ||
{ | ||
$this->children[] = $child; | ||
$child->setParent($this); | ||
|
||
return $this; | ||
} | ||
|
||
public function removeChild(Node $child) | ||
{ | ||
$this->children = array_filter($this->children, function (Node $element) use ($child) { | ||
return $child != $element; | ||
}); | ||
} | ||
|
||
public function merge(Node $sibling) | ||
{ | ||
$this->children = array_merge($this->children, $sibling->children); | ||
} | ||
|
||
/** | ||
* @return Node | ||
*/ | ||
public function getParent() | ||
{ | ||
return $this->parent; | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
<?php | ||
|
||
namespace FluxBB\Markdown\Node; | ||
|
||
interface NodeAcceptorInterface | ||
{ | ||
|
||
/** | ||
* Accept the given node as a child. | ||
* | ||
* This should ask the node for its type, and then call the appropriate method. | ||
* | ||
* @param NodeInterface $node | ||
* @return Node | ||
*/ | ||
public function accept(NodeInterface $node); | ||
|
||
public function acceptParagraph(Paragraph $paragraph); | ||
|
||
public function acceptBlockquote(Blockquote $blockquote); | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
<?php | ||
|
||
namespace FluxBB\Markdown\Node; | ||
|
||
interface NodeInterface | ||
{ | ||
|
||
/** | ||
* @param NodeAcceptorInterface $block | ||
* @return Node | ||
*/ | ||
public function proposeTo(NodeAcceptorInterface $block); | ||
|
||
} |
Oops, something went wrong.