[ Index ]

PHP Cross Reference of DokuWiki

title

Body

[close]

/inc/parser/ -> parser.php (source)

   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->