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