xref: /dokuwiki/inc/parser/parser.php (revision 47f73ecf3ecd5d2a8e07b52f22a70ce2329d24bb)
1<?php
2
3use dokuwiki\ParserMode\Base;
4use dokuwiki\ParserMode\ModeInterface;
5
6/**
7 * Define various types of modes used by the parser - they are used to
8 * populate the list of modes another mode accepts
9 */
10global $PARSER_MODES;
11$PARSER_MODES = array(
12    // containers are complex modes that can contain many other modes
13    // hr breaks the principle but they shouldn't be used in tables / lists
14    // so they are put here
15    'container'    => array('listblock','table','quote','hr'),
16
17    // some mode are allowed inside the base mode only
18    'baseonly'     => array('header'),
19
20    // modes for styling text -- footnote behaves similar to styling
21    'formatting'   => array('strong', 'emphasis', 'underline', 'monospace',
22                            'subscript', 'superscript', 'deleted', 'footnote'),
23
24    // modes where the token is simply replaced - they can not contain any
25    // other modes
26    'substition'   => array('acronym','smiley','wordblock','entity',
27                            'camelcaselink', 'internallink','media',
28                            'externallink','linebreak','emaillink',
29                            'windowssharelink','filelink','notoc',
30                            'nocache','multiplyentity','quotes','rss'),
31
32    // modes which have a start and end token but inside which
33    // no other modes should be applied
34    'protected'    => array('preformatted','code','file','php','html','htmlblock','phpblock'),
35
36    // inside this mode no wiki markup should be applied but lineendings
37    // and whitespace isn't preserved
38    'disabled'     => array('unformatted'),
39
40    // used to mark paragraph boundaries
41    'paragraphs'   => array('eol')
42);
43
44/**
45 * Sets up the Lexer with modes and points it to the Handler
46 * For an intro to the Lexer see: wiki:parser
47 */
48class Doku_Parser {
49
50    /** @var Doku_Handler */
51    protected $handler;
52
53    /** @var Doku_Lexer $lexer */
54    protected $lexer;
55
56    /** @var ModeInterface[] $modes */
57    protected $modes = array();
58
59    /** @var bool mode connections may only be set up once */
60    protected $connected = false;
61
62    /**
63     * Doku_Parser constructor.
64     *
65     * @param Doku_Handler $handler
66     */
67    public function __construct(Doku_Handler $handler) {
68        $this->handler = $handler;
69    }
70
71    /**
72     * Adds the base mode and initialized the lexer
73     *
74     * @param Base $BaseMode
75     */
76    protected function addBaseMode($BaseMode) {
77        $this->modes['base'] = $BaseMode;
78        if ( !$this->lexer ) {
79            $this->lexer = new Doku_Lexer($this->handler, 'base', true);
80        }
81        $this->modes['base']->Lexer = $this->lexer;
82    }
83
84    /**
85     * Add a new syntax element (mode) to the parser
86     *
87     * PHP preserves order of associative elements
88     * Mode sequence is important
89     *
90     * @param string $name
91     * @param ModeInterface $Mode
92     */
93    public function addMode($name, ModeInterface $Mode) {
94        if ( !isset($this->modes['base']) ) {
95            $this->addBaseMode(new Base());
96        }
97        $Mode->Lexer = $this->lexer; // FIXME should be done by setter
98        $this->modes[$name] = $Mode;
99    }
100
101    /**
102     * Connect all modes with each other
103     *
104     * This is the last step before actually parsing.
105     */
106    protected function connectModes() {
107
108        if ( $this->connected ) {
109            return;
110        }
111
112        foreach ( array_keys($this->modes) as $mode ) {
113            // Base isn't connected to anything
114            if ( $mode == 'base' ) {
115                continue;
116            }
117            $this->modes[$mode]->preConnect();
118
119            foreach ( array_keys($this->modes) as $cm ) {
120
121                if ( $this->modes[$cm]->accepts($mode) ) {
122                    $this->modes[$mode]->connectTo($cm);
123                }
124
125            }
126
127            $this->modes[$mode]->postConnect();
128        }
129
130        $this->connected = true;
131    }
132
133    /**
134     * Parses wiki syntax to instructions
135     *
136     * @param string $doc the wiki syntax text
137     * @return array instructions
138     */
139    public function parse($doc) {
140        $this->connectModes();
141        // Normalize CRs and pad doc
142        $doc = "\n".str_replace("\r\n","\n",$doc)."\n";
143        $this->lexer->parse($doc);
144        $this->handler->_finalize();
145        return $this->handler->calls;
146    }
147
148}
149