Skip to content
Snippets Groups Projects
Commit 0cecf9d5 authored by andi's avatar andi
Browse files

new parser added (define DOKU_USENEWPARSER to use it)

darcs-hash:20050331145749-9977f-f011ea6c65a278197e9087b685c635c60a204cd2.gz
parent c53ea5f2
No related branches found
No related tags found
No related merge requests found
......@@ -506,11 +506,18 @@ function parsedWiki($id,$rev='',$excuse=true){
}
}else{
if(@file_exists($file)){
$ret = io_cacheParse($file);
if(!defined('DOKU_USENEWPARSER')){
$ret = io_cacheParse($file);
}else{
msg('using new parser');
require_once(DOKU_INC.'inc/parser/action.php');
$ret = render_as_xhtml(parse_to_instructions(io_readFile($file)));
}
}elseif($excuse){
$ret = parsedLocale('newpage');
}
}
return $ret;
}
......
<?php
if(!defined('DOKU_INC')) define('DOKU_INC',realpath(dirname(__FILE__).'/../../').'/');
function parse_to_instructions($text){
require_once DOKU_INC . 'inc/parser/parser.php';
// Create the parser
$Parser = & new Doku_Parser();
// Add the Handler
$Parser->Handler = & new Doku_Handler();
// Load all the modes
$Parser->addMode('listblock',new Doku_Parser_Mode_ListBlock());
$Parser->addMode('preformatted',new Doku_Parser_Mode_Preformatted());
$Parser->addMode('notoc',new Doku_Parser_Mode_NoToc());
$Parser->addMode('header',new Doku_Parser_Mode_Header());
$Parser->addMode('table',new Doku_Parser_Mode_Table());
$formats = array (
'strong', 'emphasis', 'underline', 'monospace',
'subscript', 'superscript', 'deleted',
);
foreach ( $formats as $format ) {
$Parser->addMode($format,new Doku_Parser_Mode_Formatting($format));
}
$Parser->addMode('linebreak',new Doku_Parser_Mode_Linebreak());
$Parser->addMode('footnote',new Doku_Parser_Mode_Footnote());
$Parser->addMode('hr',new Doku_Parser_Mode_HR());
$Parser->addMode('unformatted',new Doku_Parser_Mode_Unformatted());
$Parser->addMode('php',new Doku_Parser_Mode_PHP());
$Parser->addMode('html',new Doku_Parser_Mode_HTML());
$Parser->addMode('code',new Doku_Parser_Mode_Code());
$Parser->addMode('file',new Doku_Parser_Mode_File());
$Parser->addMode('quote',new Doku_Parser_Mode_Quote());
// FIXME These need data files...
#$Parser->addMode('acronym',new Doku_Parser_Mode_Acronym(array_keys(getAcronyms())));
#$Parser->addMode('wordblock',new Doku_Parser_Mode_Wordblock(getBadWords()));
#$Parser->addMode('smiley',new Doku_Parser_Mode_Smiley(array_keys(getSmileys())));
#$Parser->addMode('entity',new Doku_Parser_Mode_Entity(array_keys(getEntities())));
$Parser->addMode('multiplyentity',new Doku_Parser_Mode_MultiplyEntity());
$Parser->addMode('quotes',new Doku_Parser_Mode_Quotes());
$Parser->addMode('camelcaselink',new Doku_Parser_Mode_CamelCaseLink());
$Parser->addMode('internallink',new Doku_Parser_Mode_InternalLink());
$Parser->addMode('media',new Doku_Parser_Mode_Media());
$Parser->addMode('externallink',new Doku_Parser_Mode_ExternalLink());
$Parser->addMode('email',new Doku_Parser_Mode_Email());
$Parser->addMode('windowssharelink',new Doku_Parser_Mode_WindowsShareLink());
$Parser->addMode('filelink',new Doku_Parser_Mode_FileLink());
$Parser->addMode('eol',new Doku_Parser_Mode_Eol());
// Do the parsing
return $Parser->parse($text);
}
function render_as_xhtml($instructions){
// Create the renderer
require_once DOKU_INC . 'inc/parser/xhtml.php';
$Renderer = & new Doku_Renderer_XHTML();
//FIXME add data
#$Renderer->smileys = getSmileys();
#$Renderer->entities = getEntities();
#$Renderer->acronyms = getAcronyms();
#$Renderer->interwiki = getInterwiki();
#$Renderer->badwords = getBadWords();
// Loop through the instructions
foreach ( $instructions as $instruction ) {
// Execute the callback against the Renderer
call_user_func_array(array(&$Renderer, $instruction[0]),$instruction[1]);
}
// Return the output
return $Renderer->doc;
}
?>
This diff is collapsed.
<?php
/**
* Author Markus Baker: http://www.lastcraft.com
* Version adapted from Simple Test: http://sourceforge.net/projects/simpletest/
* For an intro to the Lexer see:
* http://www.phppatterns.com/index.php/article/articleview/106/1/2/
* @author Marcus Baker
* @package Doku
* @subpackage Lexer
* @version $Id: lexer.php,v 1.1 2005/03/23 23:14:09 harryf Exp $
*/
/**
* Init path constant
*/
if(!defined('DOKU_INC')) define('DOKU_INC',realpath(dirname(__FILE__).'/../../').'/');
/**#@+
* lexer mode constant
*/
define("DOKU_LEXER_ENTER", 1);
define("DOKU_LEXER_MATCHED", 2);
define("DOKU_LEXER_UNMATCHED", 3);
define("DOKU_LEXER_EXIT", 4);
define("DOKU_LEXER_SPECIAL", 5);
/**#@-*/
/**
* Compounded regular expression. Any of
* the contained patterns could match and
* when one does it's label is returned.
* @package Doku
* @subpackage Lexer
*/
class Doku_LexerParallelRegex {
var $_patterns;
var $_labels;
var $_regex;
var $_case;
/**
* Constructor. Starts with no patterns.
* @param boolean $case True for case sensitive, false
* for insensitive.
* @access public
*/
function Doku_LexerParallelRegex($case) {
$this->_case = $case;
$this->_patterns = array();
$this->_labels = array();
$this->_regex = null;
}
/**
* Adds a pattern with an optional label.
* @param mixed $pattern Perl style regex. Must be UTF-8
* encoded. If its a string, the (, )
* lose their meaning unless they
* form part of a lookahead or
* lookbehind assertation.
* @param string $label Label of regex to be returned
* on a match. Label must be ASCII
* @access public
*/
function addPattern($pattern, $label = true) {
$count = count($this->_patterns);
$this->_patterns[$count] = $pattern;
$this->_labels[$count] = $label;
$this->_regex = null;
}
/**
* Attempts to match all patterns at once against
* a string.
* @param string $subject String to match against.
* @param string $match First matched portion of
* subject.
* @return boolean True on success.
* @access public
*/
function match($subject, &$match) {
if (count($this->_patterns) == 0) {
return false;
}
if (! preg_match($this->_getCompoundedRegex(), $subject, $matches)) {
$match = "";
return false;
}
$match = $matches[0];
$size = count($matches);
for ($i = 1; $i < $size; $i++) {
if ($matches[$i] && isset($this->_labels[$i - 1])) {
return $this->_labels[$i - 1];
}
}
return true;
}
/**
* Compounds the patterns into a single
* regular expression separated with the
* "or" operator. Caches the regex.
* Will automatically escape (, ) and / tokens.
* @param array $patterns List of patterns in order.
* @access private
*/
function _getCompoundedRegex() {
if ($this->_regex == null) {
for ($i = 0; $i < count($this->_patterns); $i++) {
// Replace lookaheads / lookbehinds with marker
$pattern = preg_replace(
array (
'/\(\?(i|m|s|x|U)\)/U',
'/\(\?(\-[i|m|s|x|U])\)/U',
'/\(\?\=(.*)\)/sU',
'/\(\?\!(.*)\)/sU',
'/\(\?\<\=(.*)\)/sU',
'/\(\?\<\!(.*)\)/sU',
),
array (
'<<<<SO:\\1>>>>',
'<<<<SOR:\\1>>>>',
'<<<<LA:IS:\\1>>>>',
'<<<<LA:NOT:\\1>>>>',
'<<<<LB:IS:\\1>>>>',
'<<<<LB:NOT:\\1>>>>',
),
$this->_patterns[$i]
);
// Quote the rest
$pattern = str_replace(
array('/', '(', ')'),
array('\/', '\(', '\)'),
$pattern
);
// Restore lookaheads / lookbehinds
$pattern = preg_replace(
array (
// Why five?
'<<<<<SO:(.{1})>>>>>',
'<<<<<SOR:(.{2})>>>>>',
'/<<<<LA:IS:(.*)>>>>/sU',
'/<<<<LA:NOT:(.*)>>>>/sU',
'/<<<<LB:IS:(.*)>>>>/sU',
'/<<<<LB:NOT:(.*)>>>>/sU',
),
array (
'(?\\1)',
'(?\\1)',
'(?=\\1)',
'(?!\\1)',
'(?<=\\1)',
'(?<!\\1)',
),
$pattern
);
$this->_patterns[$i] = '('.$pattern.')';
}
$this->_regex = "/" . implode("|", $this->_patterns) . "/" . $this->_getPerlMatchingFlags();
}
return $this->_regex;
}
/**
* Accessor for perl regex mode flags to use.
* @return string Perl regex flags.
* @access private
*/
function _getPerlMatchingFlags() {
return ($this->_case ? "msS" : "msSi");
}
}
/**
* States for a stack machine.
* @package Lexer
* @subpackage Lexer
*/
class Doku_LexerStateStack {
var $_stack;
/**
* Constructor. Starts in named state.
* @param string $start Starting state name.
* @access public
*/
function Doku_LexerStateStack($start) {
$this->_stack = array($start);
}
/**
* Accessor for current state.
* @return string State.
* @access public
*/
function getCurrent() {
return $this->_stack[count($this->_stack) - 1];
}
/**
* Adds a state to the stack and sets it
* to be the current state.
* @param string $state New state.
* @access public
*/
function enter($state) {
array_push($this->_stack, $state);
}
/**
* Leaves the current state and reverts
* to the previous one.
* @return boolean False if we drop off
* the bottom of the list.
* @access public
*/
function leave() {
if (count($this->_stack) == 1) {
return false;
}
array_pop($this->_stack);
return true;
}
}
/**
* Accepts text and breaks it into tokens.
* Some optimisation to make the sure the
* content is only scanned by the PHP regex
* parser once. Lexer modes must not start
* with leading underscores.
* @package Doku
* @subpackage Lexer
*/
class Doku_Lexer {
var $_regexes;
var $_parser;
var $_mode;
var $_mode_handlers;
var $_case;
/**
* Sets up the lexer in case insensitive matching
* by default.
* @param Doku_Parser $parser Handling strategy by
* reference.
* @param string $start Starting handler.
* @param boolean $case True for case sensitive.
* @access public
*/
function Doku_Lexer(&$parser, $start = "accept", $case = false) {
$this->_case = $case;
$this->_regexes = array();
$this->_parser = &$parser;
$this->_mode = &new Doku_LexerStateStack($start);
$this->_mode_handlers = array();
}
/**
* Adds a token search pattern for a particular
* parsing mode. The pattern does not change the
* current mode.
* @param string $pattern Perl style regex, but ( and )
* lose the usual meaning.
* @param string $mode Should only apply this
* pattern when dealing with
* this type of input.
* @access public
*/
function addPattern($pattern, $mode = "accept") {
if (! isset($this->_regexes[$mode])) {
$this->_regexes[$mode] = new Doku_LexerParallelRegex($this->_case);
}
$this->_regexes[$mode]->addPattern($pattern);
}
/**
* Adds a pattern that will enter a new parsing
* mode. Useful for entering parenthesis, strings,
* tags, etc.
* @param string $pattern Perl style regex, but ( and )
* lose the usual meaning.
* @param string $mode Should only apply this
* pattern when dealing with
* this type of input.
* @param string $new_mode Change parsing to this new
* nested mode.
* @access public
*/
function addEntryPattern($pattern, $mode, $new_mode) {
if (! isset($this->_regexes[$mode])) {
$this->_regexes[$mode] = new Doku_LexerParallelRegex($this->_case);
}
$this->_regexes[$mode]->addPattern($pattern, $new_mode);
}
/**
* Adds a pattern that will exit the current mode
* and re-enter the previous one.
* @param string $pattern Perl style regex, but ( and )
* lose the usual meaning.
* @param string $mode Mode to leave.
* @access public
*/
function addExitPattern($pattern, $mode) {
if (! isset($this->_regexes[$mode])) {
$this->_regexes[$mode] = new Doku_LexerParallelRegex($this->_case);
}
$this->_regexes[$mode]->addPattern($pattern, "__exit");
}
/**
* Adds a pattern that has a special mode. Acts as an entry
* and exit pattern in one go, effectively calling a special
* parser handler for this token only.
* @param string $pattern Perl style regex, but ( and )
* lose the usual meaning.
* @param string $mode Should only apply this
* pattern when dealing with
* this type of input.
* @param string $special Use this mode for this one token.
* @access public
*/
function addSpecialPattern($pattern, $mode, $special) {
if (! isset($this->_regexes[$mode])) {
$this->_regexes[$mode] = new Doku_LexerParallelRegex($this->_case);
}
$this->_regexes[$mode]->addPattern($pattern, "_$special");
}
/**
* Adds a mapping from a mode to another handler.
* @param string $mode Mode to be remapped.
* @param string $handler New target handler.
* @access public
*/
function mapHandler($mode, $handler) {
$this->_mode_handlers[$mode] = $handler;
}
/**
* Splits the page text into tokens. Will fail
* if the handlers report an error or if no
* content is consumed. If successful then each
* unparsed and parsed token invokes a call to the
* held listener.
* @param string $raw Raw HTML text.
* @return boolean True on success, else false.
* @access public
*/
function parse($raw) {
if (! isset($this->_parser)) {
return false;
}
$initialLength = strlen($raw);
$length = $initialLength;
$pos = 0;
while (is_array($parsed = $this->_reduce($raw))) {
list($unmatched, $matched, $mode) = $parsed;
$currentLength = strlen($raw);
$matchPos = $initialLength - $currentLength - strlen($matched);
if (! $this->_dispatchTokens($unmatched, $matched, $mode, $pos, $matchPos)) {
return false;
}
if ($currentLength == $length) {
return false;
}
$length = $currentLength;
$pos = $initialLength - $currentLength;
}
if (!$parsed) {
return false;
}
return $this->_invokeParser($raw, DOKU_LEXER_UNMATCHED, $pos);
}
/**
* Sends the matched token and any leading unmatched
* text to the parser changing the lexer to a new
* mode if one is listed.
* @param string $unmatched Unmatched leading portion.
* @param string $matched Actual token match.
* @param string $mode Mode after match. A boolean
* false mode causes no change.
* @param int $pos Current byte index location in raw doc
* thats being parsed
* @return boolean False if there was any error
* from the parser.
* @access private
*/
function _dispatchTokens($unmatched, $matched, $mode = false, $initialPos, $matchPos) {
if (! $this->_invokeParser($unmatched, DOKU_LEXER_UNMATCHED, $initialPos) ){
return false;
}
if ($this->_isModeEnd($mode)) {
if (! $this->_invokeParser($matched, DOKU_LEXER_EXIT, $matchPos)) {
return false;
}
return $this->_mode->leave();
}
if ($this->_isSpecialMode($mode)) {
$this->_mode->enter($this->_decodeSpecial($mode));
if (! $this->_invokeParser($matched, DOKU_LEXER_SPECIAL, $matchPos)) {
return false;
}
return $this->_mode->leave();
}
if (is_string($mode)) {
$this->_mode->enter($mode);
return $this->_invokeParser($matched, DOKU_LEXER_ENTER, $matchPos);
}
return $this->_invokeParser($matched, DOKU_LEXER_MATCHED, $matchPos);
}
/**
* Tests to see if the new mode is actually to leave
* the current mode and pop an item from the matching
* mode stack.
* @param string $mode Mode to test.
* @return boolean True if this is the exit mode.
* @access private
*/
function _isModeEnd($mode) {
return ($mode === "__exit");
}
/**
* Test to see if the mode is one where this mode
* is entered for this token only and automatically
* leaves immediately afterwoods.
* @param string $mode Mode to test.
* @return boolean True if this is the exit mode.
* @access private
*/
function _isSpecialMode($mode) {
return (strncmp($mode, "_", 1) == 0);
}
/**
* Strips the magic underscore marking single token
* modes.
* @param string $mode Mode to decode.
* @return string Underlying mode name.
* @access private
*/
function _decodeSpecial($mode) {
return substr($mode, 1);
}
/**
* Calls the parser method named after the current
* mode. Empty content will be ignored. The lexer
* has a parser handler for each mode in the lexer.
* @param string $content Text parsed.
* @param boolean $is_match Token is recognised rather
* than unparsed data.
* @param int $pos Current byte index location in raw doc
* thats being parsed
* @access private
*/
function _invokeParser($content, $is_match, $pos) {
if (($content === "") || ($content === false)) {
return true;
}
$handler = $this->_mode->getCurrent();
if (isset($this->_mode_handlers[$handler])) {
$handler = $this->_mode_handlers[$handler];
}
return $this->_parser->$handler($content, $is_match, $pos);
}
/**
* Tries to match a chunk of text and if successful
* removes the recognised chunk and any leading
* unparsed data. Empty strings will not be matched.
* @param string $raw The subject to parse. This is the
* content that will be eaten.
* @return array Three item list of unparsed
* content followed by the
* recognised token and finally the
* action the parser is to take.
* True if no match, false if there
* is a parsing error.
* @access private
*/
function _reduce(&$raw) {
if (! isset($this->_regexes[$this->_mode->getCurrent()])) {
return false;
}
if ($raw === "") {
return true;
}
if ($action = $this->_regexes[$this->_mode->getCurrent()]->match($raw, $match)) {
$unparsed_character_count = strpos($raw, $match);
$unparsed = substr($raw, 0, $unparsed_character_count);
$raw = substr($raw, $unparsed_character_count + strlen($match));
return array($unparsed, $match, $action);
}
return true;
}
}
/**
* Escapes regex characters other than (, ) and /
* @TODO
*/
function Doku_Lexer_Escape($str) {
//$str = addslashes($str);
$chars = array(
'/\./',
'/\\\\/',
'/\+/',
'/\*/',
'/\?/',
'/\[/',
'/\^/',
'/\]/',
'/\$/',
'/\{/',
'/\}/',
'/\=/',
'/\!/',
'/\</',
'/\>/',
'/\|/',
'/\:/'
);
$escaped = array(
'\.',
'\\\\\\\\',
'\+',
'\*',
'\?',
'\[',
'\^',
'\]',
'\$',
'\{',
'\}',
'\=',
'\!',
'\<',
'\>',
'\|',
'\:'
);
return preg_replace($chars, $escaped, $str);
}
?>
This diff is collapsed.
<?php
class Doku_Renderer {
function document_start() {}
function document_end() {}
function toc_open() {}
function tocbranch_open($level) {}
function tocitem_open($level, $empty = FALSE) {}
function tocelement($level, $title) {}
function tocitem_close($level) {}
function tocbranch_close($level) {}
function toc_close() {}
function header($text, $level) {}
function section_open($level) {}
function section_close() {}
function cdata($text) {}
function p_open() {}
function p_close() {}
function linebreak() {}
function hr() {}
function strong_open() {}
function strong_close() {}
function emphasis_open() {}
function emphasis_close() {}
function underline_open() {}
function underline_close() {}
function monospace_open() {}
function monospace_close() {}
function subscript_open() {}
function subscript_close() {}
function superscript_open() {}
function superscript_close() {}
function deleted_open() {}
function deleted_close() {}
function footnote_open() {}
function footnote_close() {}
function listu_open() {}
function listu_close() {}
function listo_open() {}
function listo_close() {}
function listitem_open($level) {}
function listitem_close() {}
function listcontent_open($level) {}
function listcontent_close() {}
function unformatted($text) {}
function php($text) {}
function html($text) {}
function preformatted($text) {}
function file($text) {}
function quote_open() {}
function quote_close() {}
function code($text, $lang = NULL) {}
function acronym($acronym) {}
function smiley($smiley) {}
function wordblock($word) {}
function entity($entity) {}
// 640x480 ($x=640, $y=480)
function multiplyentity($x, $y) {}
function singlequoteopening() {}
function singlequoteclosing() {}
function doublequoteopening() {}
function doublequoteclosing() {}
// $link like 'SomePage'
function camelcaselink($link) {}
// $link like 'wikie:syntax', $title could be an array (media)
function internallink($link, $title = NULL) {}
// $link is full URL with scheme, $title could be an array (media)
function externallink($link, $title = NULL) {}
// $link is the original link - probably not much use
// $wikiName is an indentifier for the wiki
// $wikiUri is the URL fragment to append to some known URL
function interwikilink($link, $title = NULL, $wikiName, $wikiUri) {}
// Link to file on users OS, $title could be an array (media)
function filelink($link, $title = NULL) {}
// Link to a Windows share, , $title could be an array (media)
function windowssharelink($link, $title = NULL) {}
function email($address, $title = NULL) {}
function internalmedialink (
$src,$title=NULL,$align=NULL,$width=NULL,$height=NULL,$cache=NULL
) {}
function externalmedialink(
$src,$title=NULL,$align=NULL,$width=NULL,$height=NULL,$cache=NULL
) {}
function table_open($maxcols = NULL, $numrows = NULL){}
function table_close(){}
function tablerow_open(){}
function tablerow_close(){}
function tableheader_open($colspan = 1, $align = NULL){}
function tableheader_close(){}
function tablecell_open($colspan = 1, $align = NULL){}
function tablecell_close(){}
}
?>
<?php
if(!defined('DOKU_INC')) define('DOKU_INC',realpath(dirname(__FILE__).'/../../').'/');
require_once DOKU_INC . 'inc/parser/renderer.php';
class Doku_Renderer_SpamCheck extends Doku_Renderer {
// This should be populated by the code executing the instructions
var $currentCall;
// An array of instructions that contain spam
var $spamFound = array();
// pcre pattern for finding spam
var $spamPattern = '#^$#';
function internallink($link, $title = NULL) {
$this->__checkTitle($title);
}
function externallink($link, $title = NULL) {
$this->__checkLinkForSpam($link);
$this->__checkTitle($title);
}
function interwikilink($link, $title = NULL) {
$this->__checkTitle($title);
}
function filelink($link, $title = NULL) {
$this->__checkLinkForSpam($link);
$this->__checkTitle($title);
}
function windowssharelink($link, $title = NULL) {
$this->__checkLinkForSpam($link);
$this->__checkTitle($title);
}
function email($address, $title = NULL) {
$this->__checkLinkForSpam($address);
$this->__checkTitle($title);
}
function internalmedialink ($src) {
$this->__checkLinkForSpam($src);
}
function externalmedialink($src) {
$this->__checkLinkForSpam($src);
}
function __checkTitle($title) {
if ( is_array($title) && isset($title['src'])) {
$this->__checkLinkForSpam($title['src']);
}
}
// Pattern matching happens here
/**
* @TODO What about links like www.google.com - no http://
*/
function __checkLinkForSpam($link) {
if( preg_match($this->spamPattern,$link) ) {
$spam = $this->currentCall;
$spam[3] = $link;
$this->spamFound[] = $spam;
}
}
}
?>
This diff is collapsed.
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment