aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/inc/Parsing/Parser.php
blob: 018f45eaa80b45cb7d0a170481f7ce792dfba2ac (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
<?php

namespace dokuwiki\Parsing;

use dokuwiki\Debug\DebugHelper;
use Doku_Handler;
use dokuwiki\Parsing\Lexer\Lexer;
use dokuwiki\Parsing\ParserMode\Base;
use dokuwiki\Parsing\ParserMode\ModeInterface;

/**
 * Sets up the Lexer with modes and points it to the Handler
 * For an intro to the Lexer see: wiki:parser
 */
class Parser
{
    /** @var Doku_Handler */
    protected $handler;

    /** @var Lexer $lexer */
    protected $lexer;

    /** @var ModeInterface[] $modes */
    protected $modes = [];

    /** @var bool mode connections may only be set up once */
    protected $connected = false;

    /**
     * dokuwiki\Parsing\Doku_Parser constructor.
     *
     * @param Doku_Handler $handler
     */
    public function __construct(Doku_Handler $handler)
    {
        $this->handler = $handler;
    }

    /**
     * Adds the base mode and initialized the lexer
     *
     * @param Base $BaseMode
     */
    protected function addBaseMode($BaseMode)
    {
        $this->modes['base'] = $BaseMode;
        if (!$this->lexer) {
            $this->lexer = new Lexer($this->handler, 'base', true);
        }
        $this->modes['base']->Lexer = $this->lexer;
    }

    /**
     * Add a new syntax element (mode) to the parser
     *
     * PHP preserves order of associative elements
     * Mode sequence is important
     *
     * @param string $name
     * @param ModeInterface $Mode
     */
    public function addMode($name, ModeInterface $Mode)
    {
        if (!isset($this->modes['base'])) {
            $this->addBaseMode(new Base());
        }
        $Mode->Lexer = $this->lexer; // FIXME should be done by setter
        $this->modes[$name] = $Mode;
    }

    /**
     * Connect all modes with each other
     *
     * This is the last step before actually parsing.
     */
    protected function connectModes()
    {

        if ($this->connected) {
            return;
        }

        foreach (array_keys($this->modes) as $mode) {
            // Base isn't connected to anything
            if ($mode == 'base') {
                continue;
            }
            $this->modes[$mode]->preConnect();

            foreach (array_keys($this->modes) as $cm) {
                if ($this->modes[$cm]->accepts($mode)) {
                    $this->modes[$mode]->connectTo($cm);
                }
            }

            $this->modes[$mode]->postConnect();
        }

        $this->connected = true;
    }

    /**
     * Parses wiki syntax to instructions
     *
     * @param string $doc the wiki syntax text
     * @return array instructions
     */
    public function parse($doc)
    {
        $this->connectModes();
        // Normalize CRs and pad doc
        $doc = "\n" . str_replace("\r\n", "\n", $doc) . "\n";
        $this->lexer->parse($doc);

        if (!method_exists($this->handler, 'finalize')) {
            /** @deprecated 2019-10 we have a legacy handler from a plugin, assume legacy _finalize exists */

            DebugHelper::dbgCustomDeprecationEvent(
                'finalize()',
                get_class($this->handler) . '::_finalize()',
                __METHOD__,
                __FILE__,
                __LINE__
            );
            $this->handler->_finalize();
        } else {
            $this->handler->finalize();
        }
        return $this->handler->calls;
    }
}