| [ Index ] |
PHP Cross Reference of DokuWiki |
[Summary view] [Print] [Text view]
1 <?php 2 3 if(!defined('DOKU_INC')) define('DOKU_INC',fullpath(dirname(__FILE__).'/../../').'/'); 4 5 require_once DOKU_INC . 'inc/parser/lexer.php'; 6 require_once DOKU_INC . 'inc/parser/handler.php'; 7 8 9 /** 10 * Define various types of modes used by the parser - they are used to 11 * populate the list of modes another mode accepts 12 */ 13 global $PARSER_MODES; 14 $PARSER_MODES = array( 15 // containers are complex modes that can contain many other modes 16 // hr breaks the principle but they shouldn't be used in tables / lists 17 // so they are put here 18 'container' => array('listblock','table','quote','hr'), 19 20 // some mode are allowed inside the base mode only 21 'baseonly' => array('header'), 22 23 // modes for styling text -- footnote behaves similar to styling 24 'formatting' => array('strong', 'emphasis', 'underline', 'monospace', 25 'subscript', 'superscript', 'deleted', 'footnote'), 26 27 // modes where the token is simply replaced - they can not contain any 28 // other modes 29 'substition' => array('acronym','smiley','wordblock','entity', 30 'camelcaselink', 'internallink','media', 31 'externallink','linebreak','emaillink', 32 'windowssharelink','filelink','notoc', 33 'nocache','multiplyentity','quotes','rss'), 34 35 // modes which have a start and end token but inside which 36 // no other modes should be applied 37 'protected' => array('preformatted','code','file','php','html','htmlblock','phpblock'), 38 39 // inside this mode no wiki markup should be applied but lineendings 40 // and whitespace isn't preserved 41 'disabled' => array('unformatted'), 42 43 // used to mark paragraph boundaries 44 'paragraphs' => array('eol') 45 ); 46 47 //------------------------------------------------------------------- 48 49 /** 50 * Sets up the Lexer with modes and points it to the Handler 51 * For an intro to the Lexer see: wiki:parser 52 */ 53 class Doku_Parser { 54 55 var $Handler; 56 57 var $Lexer; 58 59 var $modes = array(); 60 61 var $connected = false; 62 63 function addBaseMode(& $BaseMode) { 64 $this->modes['base'] = & $BaseMode; 65 if ( !$this->Lexer ) { 66 $this->Lexer = & new Doku_Lexer($this->Handler,'base', true); 67 } 68 $this->modes['base']->Lexer = & $this->Lexer; 69 } 70 71 /** 72 * PHP preserves order of associative elements 73 * Mode sequence is important 74 */ 75 function addMode($name, & $Mode) { 76 if ( !isset($this->modes['base']) ) { 77 $this->addBaseMode(new Doku_Parser_Mode_base()); 78 } 79 $Mode->Lexer = & $this->Lexer; 80 $this->modes[$name] = & $Mode; 81 } 82 83 function connectModes() { 84 85 if ( $this->connected ) { 86 return; 87 } 88 89 foreach ( array_keys($this->modes) as $mode ) { 90 91 // Base isn't connected to anything 92 if ( $mode == 'base' ) { 93 continue; 94 } 95 96 $this->modes[$mode]->preConnect(); 97 98 foreach ( array_keys($this->modes) as $cm ) { 99 100 if ( $this->modes[$cm]->accepts($mode) ) { 101 $this->modes[$mode]->connectTo($cm); 102 } 103 104 } 105 106 $this->modes[$mode]->postConnect(); 107 } 108 109 $this->connected = true; 110 } 111 112 function parse($doc) { 113 if ( $this->Lexer ) { 114 $this->connectModes(); 115 // Normalize CRs and pad doc 116 $doc = "\n".str_replace("\r\n","\n",$doc)."\n"; 117 $this->Lexer->parse($doc); 118 $this->Handler->_finalize(); 119 return $this->Handler->calls; 120 } else { 121 return false; 122 } 123 } 124 125 } 126 127 //------------------------------------------------------------------- 128 /** 129 * This class and all the subclasses below are 130 * used to reduce the effort required to register 131 * modes with the Lexer. For performance these 132 * could all be eliminated later perhaps, or 133 * the Parser could be serialized to a file once 134 * all modes are registered 135 * 136 * @author Harry Fuecks <hfuecks@gmail.com> 137 */ 138 class Doku_Parser_Mode { 139 140 var $Lexer; 141 142 var $allowedModes = array(); 143 144 // returns a number used to determine in which order modes are added 145 function getSort() { 146 trigger_error('getSort() not implemented in '.get_class($this), E_USER_WARNING); 147 } 148 149 // Called before any calls to connectTo 150 function preConnect() {} 151 152 // Connects the mode 153 function connectTo($mode) {} 154 155 // Called after all calls to connectTo 156 function postConnect() {} 157 158 function accepts($mode) { 159 return in_array($mode, (array) $this->allowedModes ); 160 } 161 162 } 163 164 //------------------------------------------------------------------- 165 class Doku_Parser_Mode_base extends Doku_Parser_Mode { 166 167 function Doku_Parser_Mode_base() { 168 global $PARSER_MODES; 169 170 $this->allowedModes = array_merge ( 171 $PARSER_MODES['container'], 172 $PARSER_MODES['baseonly'], 173 $PARSER_MODES['paragraphs'], 174 $PARSER_MODES['formatting'], 175 $PARSER_MODES['substition'], 176 $PARSER_MODES['protected'], 177 $PARSER_MODES['disabled'] 178 ); 179 } 180 181 function getSort() { 182 return 0; 183 } 184 } 185 186 //------------------------------------------------------------------- 187 class Doku_Parser_Mode_footnote extends Doku_Parser_Mode { 188 189 function Doku_Parser_Mode_footnote() { 190 global $PARSER_MODES; 191 192 $this->allowedModes = array_merge ( 193 $PARSER_MODES['container'], 194 $PARSER_MODES['formatting'], 195 $PARSER_MODES['substition'], 196 $PARSER_MODES['protected'], 197 $PARSER_MODES['disabled'] 198 ); 199 200 unset($this->allowedModes[array_search('footnote', $this->allowedModes)]); 201 } 202 203 function connectTo($mode) { 204 $this->Lexer->addEntryPattern( 205 '\x28\x28(?=.*\x29\x29)',$mode,'footnote' 206 ); 207 } 208 209 function postConnect() { 210 $this->Lexer->addExitPattern( 211 '\x29\x29','footnote' 212 ); 213 } 214 215 function getSort() { 216 return 150; 217 } 218 } 219 220 //------------------------------------------------------------------- 221 class Doku_Parser_Mode_header extends Doku_Parser_Mode { 222 223 function preConnect() { 224 //we're not picky about the closing ones, two are enough 225 $this->Lexer->addSpecialPattern( 226 '[ \t]*={2,}[^\n]+={2,}[ \t]*(?=\n)', 227 'base', 228 'header' 229 ); 230 } 231 232 function getSort() { 233 return 50; 234 } 235 } 236 237 //------------------------------------------------------------------- 238 class Doku_Parser_Mode_notoc extends Doku_Parser_Mode { 239 240 function connectTo($mode) { 241 $this->Lexer->addSpecialPattern('~~NOTOC~~',$mode,'notoc'); 242 } 243 244 function getSort() { 245 return 30; 246 } 247 } 248 249 //------------------------------------------------------------------- 250 class Doku_Parser_Mode_nocache extends Doku_Parser_Mode { 251 252 function connectTo($mode) { 253 $this->Lexer->addSpecialPattern('~~NOCACHE~~',$mode,'nocache'); 254 } 255 256 function getSort() { 257 return 40; 258 } 259 } 260 261 //------------------------------------------------------------------- 262 class Doku_Parser_Mode_linebreak extends Doku_Parser_Mode { 263 264 function connectTo($mode) { 265 $this->Lexer->addSpecialPattern('\x5C{2}(?=\s)',$mode,'linebreak'); 266 } 267 268 function getSort() { 269 return 140; 270 } 271 } 272 273 //------------------------------------------------------------------- 274 class Doku_Parser_Mode_eol extends Doku_Parser_Mode { 275 276 function connectTo($mode) { 277 $badModes = array('listblock','table'); 278 if ( in_array($mode, $badModes) ) { 279 return; 280 } 281 $this->Lexer->addSpecialPattern('\n',$mode,'eol'); 282 } 283 284 function getSort() { 285 return 370; 286 } 287 } 288 289 //------------------------------------------------------------------- 290 class Doku_Parser_Mode_hr extends Doku_Parser_Mode { 291 292 function connectTo($mode) { 293 $this->Lexer->addSpecialPattern('\n[ \t]*-{4,}[ \t]*(?=\n)',$mode,'hr'); 294 } 295 296 function getSort() { 297 return 160; 298 } 299 } 300 301 //------------------------------------------------------------------- 302 class Doku_Parser_Mode_formatting extends Doku_Parser_Mode { 303 var $type; 304 305 var $formatting = array ( 306 'strong' => array ( 307 'entry'=>'\*\*(?=.*\*\*)', 308 'exit'=>'\*\*', 309 'sort'=>70 310 ), 311 312 'emphasis'=> array ( 313 'entry'=>'//(?=[^\x00]*[^:])', //hack for bugs #384 #763 #1468 314 'exit'=>'//', 315 'sort'=>80 316 ), 317 318 'underline'=> array ( 319 'entry'=>'__(?=.*__)', 320 'exit'=>'__', 321 'sort'=>90 322 ), 323 324 'monospace'=> array ( 325 'entry'=>'\x27\x27(?=.*\x27\x27)', 326 'exit'=>'\x27\x27', 327 'sort'=>100 328 ), 329 330 'subscript'=> array ( 331 'entry'=>'<sub>(?=.*</sub>)', 332 'exit'=>'</sub>', 333 'sort'=>110 334 ), 335 336 'superscript'=> array ( 337 'entry'=>'<sup>(?=.*</sup>)', 338 'exit'=>'</sup>', 339 'sort'=>120 340 ), 341 342 'deleted'=> array ( 343 'entry'=>'<del>(?=.*</del>)', 344 'exit'=>'</del>', 345 'sort'=>130 346 ), 347 ); 348 349 function Doku_Parser_Mode_formatting($type) { 350 global $PARSER_MODES; 351 352 if ( !array_key_exists($type, $this->formatting) ) { 353 trigger_error('Invalid formatting type '.$type, E_USER_WARNING); 354 } 355 356 $this->type = $type; 357 358 // formatting may contain other formatting but not it self 359 $modes = $PARSER_MODES['formatting']; 360 $key = array_search($type, $modes); 361 if ( is_int($key) ) { 362 unset($modes[$key]); 363 } 364 365 $this->allowedModes = array_merge ( 366 $modes, 367 $PARSER_MODES['substition'], 368 $PARSER_MODES['disabled'] 369 ); 370 } 371 372 function connectTo($mode) { 373 374 // Can't nest formatting in itself 375 if ( $mode == $this->type ) { 376 return; 377 } 378 379 $this->Lexer->addEntryPattern( 380 $this->formatting[$this->type]['entry'], 381 $mode, 382 $this->type 383 ); 384 } 385 386 function postConnect() { 387 388 $this->Lexer->addExitPattern( 389 $this->formatting[$this->type]['exit'], 390 $this->type 391 ); 392 393 } 394 395 function getSort() { 396 return $this->formatting[$this->type]['sort']; 397 } 398 } 399 400 //------------------------------------------------------------------- 401 class Doku_Parser_Mode_listblock extends Doku_Parser_Mode { 402 403 function Doku_Parser_Mode_listblock() { 404 global $PARSER_MODES; 405 406 $this->allowedModes = array_merge ( 407 $PARSER_MODES['formatting'], 408 $PARSER_MODES['substition'], 409 $PARSER_MODES['disabled'], 410 $PARSER_MODES['protected'] #XXX new 411 ); 412 413 // $this->allowedModes[] = 'footnote'; 414 } 415 416 function connectTo($mode) { 417 $this->Lexer->addEntryPattern('\n {2,}[\-\*]',$mode,'listblock'); 418 $this->Lexer->addEntryPattern('\n\t{1,}[\-\*]',$mode,'listblock'); 419 420 $this->Lexer->addPattern('\n {2,}[\-\*]','listblock'); 421 $this->Lexer->addPattern('\n\t{1,}[\-\*]','listblock'); 422 423 } 424 425 function postConnect() { 426 $this->Lexer->addExitPattern('\n','listblock'); 427 } 428 429 function getSort() { 430 return 10; 431 } 432 } 433 434 //------------------------------------------------------------------- 435 class Doku_Parser_Mode_table extends Doku_Parser_Mode { 436 437 function Doku_Parser_Mode_table() { 438 global $PARSER_MODES; 439 440 $this->allowedModes = array_merge ( 441 $PARSER_MODES['formatting'], 442 $PARSER_MODES['substition'], 443 $PARSER_MODES['disabled'], 444 $PARSER_MODES['protected'] 445 ); 446 } 447 448 function connectTo($mode) { 449 $this->Lexer->addEntryPattern('\n\^',$mode,'table'); 450 $this->Lexer->addEntryPattern('\n\|',$mode,'table'); 451 } 452 453 function postConnect() { 454 $this->Lexer->addPattern('\n\^','table'); 455 $this->Lexer->addPattern('\n\|','table'); 456 #$this->Lexer->addPattern(' {2,}','table'); 457 $this->Lexer->addPattern('[\t ]+','table'); 458 $this->Lexer->addPattern('\^','table'); 459 $this->Lexer->addPattern('\|','table'); 460 $this->Lexer->addExitPattern('\n','table'); 461 } 462 463 function getSort() { 464 return 60; 465 } 466 } 467 468 //------------------------------------------------------------------- 469 class Doku_Parser_Mode_unformatted extends Doku_Parser_Mode { 470 471 function connectTo($mode) { 472 $this->Lexer->addEntryPattern('<nowiki>(?=.*</nowiki>)',$mode,'unformatted'); 473 $this->Lexer->addEntryPattern('%%(?=.*%%)',$mode,'unformattedalt'); 474 } 475 476 function postConnect() { 477 $this->Lexer->addExitPattern('</nowiki>','unformatted'); 478 $this->Lexer->addExitPattern('%%','unformattedalt'); 479 $this->Lexer->mapHandler('unformattedalt','unformatted'); 480 } 481 482 function getSort() { 483 return 170; 484 } 485 } 486 487 //------------------------------------------------------------------- 488 class Doku_Parser_Mode_php extends Doku_Parser_Mode { 489 490 function connectTo($mode) { 491 $this->Lexer->addEntryPattern('<php>(?=.*</php>)',$mode,'php'); 492 $this->Lexer->addEntryPattern('<PHP>(?=.*</PHP>)',$mode,'phpblock'); 493 } 494 495 function postConnect() { 496 $this->Lexer->addExitPattern('</php>','php'); 497 $this->Lexer->addExitPattern('</PHP>','phpblock'); 498 } 499 500 function getSort() { 501 return 180; 502 } 503 } 504 505 //------------------------------------------------------------------- 506 class Doku_Parser_Mode_html extends Doku_Parser_Mode { 507 508 function connectTo($mode) { 509 $this->Lexer->addEntryPattern('<html>(?=.*</html>)',$mode,'html'); 510 $this->Lexer->addEntryPattern('<HTML>(?=.*</HTML>)',$mode,'htmlblock'); 511 } 512 513 function postConnect() { 514 $this->Lexer->addExitPattern('</html>','html'); 515 $this->Lexer->addExitPattern('</HTML>','htmlblock'); 516 } 517 518 function getSort() { 519 return 190; 520 } 521 } 522 523 //------------------------------------------------------------------- 524 class Doku_Parser_Mode_preformatted extends Doku_Parser_Mode { 525 526 function connectTo($mode) { 527 // Has hard coded awareness of lists... 528 $this->Lexer->addEntryPattern('\n (?![\*\-])',$mode,'preformatted'); 529 $this->Lexer->addEntryPattern('\n\t(?![\*\-])',$mode,'preformatted'); 530 531 // How to effect a sub pattern with the Lexer! 532 $this->Lexer->addPattern('\n ','preformatted'); 533 $this->Lexer->addPattern('\n\t','preformatted'); 534 535 } 536 537 function postConnect() { 538 $this->Lexer->addExitPattern('\n','preformatted'); 539 } 540 541 function getSort() { 542 return 20; 543 } 544 } 545 546 //------------------------------------------------------------------- 547 class Doku_Parser_Mode_code extends Doku_Parser_Mode { 548 549 function connectTo($mode) { 550 $this->Lexer->addEntryPattern('<code(?=.*</code>)',$mode,'code'); 551 } 552 553 function postConnect() { 554 $this->Lexer->addExitPattern('</code>','code'); 555 } 556 557 function getSort() { 558 return 200; 559 } 560 } 561 562 //------------------------------------------------------------------- 563 class Doku_Parser_Mode_file extends Doku_Parser_Mode { 564 565 function connectTo($mode) { 566 $this->Lexer->addEntryPattern('<file>(?=.*</file>)',$mode,'file'); 567 } 568 569 function postConnect() { 570 $this->Lexer->addExitPattern('</file>','file'); 571 } 572 573 function getSort() { 574 return 210; 575 } 576 } 577 578 //------------------------------------------------------------------- 579 class Doku_Parser_Mode_quote extends Doku_Parser_Mode { 580 581 function Doku_Parser_Mode_quote() { 582 global $PARSER_MODES; 583 584 $this->allowedModes = array_merge ( 585 $PARSER_MODES['formatting'], 586 $PARSER_MODES['substition'], 587 $PARSER_MODES['disabled'], 588 $PARSER_MODES['protected'] #XXX new 589 ); 590 #$this->allowedModes[] = 'footnote'; 591 #$this->allowedModes[] = 'preformatted'; 592 #$this->allowedModes[] = 'unformatted'; 593 } 594 595 function connectTo($mode) { 596 $this->Lexer->addEntryPattern('\n>{1,}',$mode,'quote'); 597 } 598 599 function postConnect() { 600 $this->Lexer->addPattern('\n>{1,}','quote'); 601 $this->Lexer->addExitPattern('\n','quote'); 602 } 603 604 function getSort() { 605 return 220; 606 } 607 } 608 609 //------------------------------------------------------------------- 610 class Doku_Parser_Mode_acronym extends Doku_Parser_Mode { 611 // A list 612 var $acronyms = array(); 613 var $pattern = ''; 614 615 function Doku_Parser_Mode_acronym($acronyms) { 616 $this->acronyms = $acronyms; 617 } 618 619 function preConnect() { 620 if(!count($this->