xref: /dokuwiki/inc/Parsing/Parser.php (revision 1b008e8700714b7de7e620d57eccc70c8b206512)
1d4d8fb18SAndreas Gohr<?php
2d4d8fb18SAndreas Gohr
3d4d8fb18SAndreas Gohrnamespace dokuwiki\Parsing;
4d4d8fb18SAndreas Gohr
5d4d8fb18SAndreas Gohruse Doku_Handler;
6d4d8fb18SAndreas Gohruse dokuwiki\Parsing\Lexer\Lexer;
7d4d8fb18SAndreas Gohruse dokuwiki\Parsing\ParserMode\Base;
8d4d8fb18SAndreas Gohruse dokuwiki\Parsing\ParserMode\ModeInterface;
9d4d8fb18SAndreas Gohr
10d4d8fb18SAndreas Gohr/**
11d4d8fb18SAndreas Gohr * Sets up the Lexer with modes and points it to the Handler
12d4d8fb18SAndreas Gohr * For an intro to the Lexer see: wiki:parser
13d4d8fb18SAndreas Gohr */
14d4d8fb18SAndreas Gohrclass Parser {
15d4d8fb18SAndreas Gohr
16d4d8fb18SAndreas Gohr    /** @var Doku_Handler */
17d4d8fb18SAndreas Gohr    protected $handler;
18d4d8fb18SAndreas Gohr
19d4d8fb18SAndreas Gohr    /** @var Lexer $lexer */
20d4d8fb18SAndreas Gohr    protected $lexer;
21d4d8fb18SAndreas Gohr
22d4d8fb18SAndreas Gohr    /** @var ModeInterface[] $modes */
23d4d8fb18SAndreas Gohr    protected $modes = array();
24d4d8fb18SAndreas Gohr
25d4d8fb18SAndreas Gohr    /** @var bool mode connections may only be set up once */
26d4d8fb18SAndreas Gohr    protected $connected = false;
27d4d8fb18SAndreas Gohr
28d4d8fb18SAndreas Gohr    /**
29d4d8fb18SAndreas Gohr     * dokuwiki\Parsing\Doku_Parser constructor.
30d4d8fb18SAndreas Gohr     *
31d4d8fb18SAndreas Gohr     * @param Doku_Handler $handler
32d4d8fb18SAndreas Gohr     */
33d4d8fb18SAndreas Gohr    public function __construct(Doku_Handler $handler) {
34d4d8fb18SAndreas Gohr        $this->handler = $handler;
35d4d8fb18SAndreas Gohr    }
36d4d8fb18SAndreas Gohr
37d4d8fb18SAndreas Gohr    /**
38d4d8fb18SAndreas Gohr     * Adds the base mode and initialized the lexer
39d4d8fb18SAndreas Gohr     *
40d4d8fb18SAndreas Gohr     * @param Base $BaseMode
41d4d8fb18SAndreas Gohr     */
42d4d8fb18SAndreas Gohr    protected function addBaseMode($BaseMode) {
43d4d8fb18SAndreas Gohr        $this->modes['base'] = $BaseMode;
44d4d8fb18SAndreas Gohr        if(!$this->lexer) {
45d4d8fb18SAndreas Gohr            $this->lexer = new Lexer($this->handler, 'base', true);
46d4d8fb18SAndreas Gohr        }
47d4d8fb18SAndreas Gohr        $this->modes['base']->Lexer = $this->lexer;
48d4d8fb18SAndreas Gohr    }
49d4d8fb18SAndreas Gohr
50d4d8fb18SAndreas Gohr    /**
51d4d8fb18SAndreas Gohr     * Add a new syntax element (mode) to the parser
52d4d8fb18SAndreas Gohr     *
53d4d8fb18SAndreas Gohr     * PHP preserves order of associative elements
54d4d8fb18SAndreas Gohr     * Mode sequence is important
55d4d8fb18SAndreas Gohr     *
56d4d8fb18SAndreas Gohr     * @param string $name
57d4d8fb18SAndreas Gohr     * @param ModeInterface $Mode
58d4d8fb18SAndreas Gohr     */
59d4d8fb18SAndreas Gohr    public function addMode($name, ModeInterface $Mode) {
60d4d8fb18SAndreas Gohr        if(!isset($this->modes['base'])) {
61d4d8fb18SAndreas Gohr            $this->addBaseMode(new Base());
62d4d8fb18SAndreas Gohr        }
63d4d8fb18SAndreas Gohr        $Mode->Lexer = $this->lexer; // FIXME should be done by setter
64d4d8fb18SAndreas Gohr        $this->modes[$name] = $Mode;
65d4d8fb18SAndreas Gohr    }
66d4d8fb18SAndreas Gohr
67d4d8fb18SAndreas Gohr    /**
68d4d8fb18SAndreas Gohr     * Connect all modes with each other
69d4d8fb18SAndreas Gohr     *
70d4d8fb18SAndreas Gohr     * This is the last step before actually parsing.
71d4d8fb18SAndreas Gohr     */
72d4d8fb18SAndreas Gohr    protected function connectModes() {
73d4d8fb18SAndreas Gohr
74d4d8fb18SAndreas Gohr        if($this->connected) {
75d4d8fb18SAndreas Gohr            return;
76d4d8fb18SAndreas Gohr        }
77d4d8fb18SAndreas Gohr
78d4d8fb18SAndreas Gohr        foreach(array_keys($this->modes) as $mode) {
79d4d8fb18SAndreas Gohr            // Base isn't connected to anything
80d4d8fb18SAndreas Gohr            if($mode == 'base') {
81d4d8fb18SAndreas Gohr                continue;
82d4d8fb18SAndreas Gohr            }
83d4d8fb18SAndreas Gohr            $this->modes[$mode]->preConnect();
84d4d8fb18SAndreas Gohr
85d4d8fb18SAndreas Gohr            foreach(array_keys($this->modes) as $cm) {
86d4d8fb18SAndreas Gohr
87d4d8fb18SAndreas Gohr                if($this->modes[$cm]->accepts($mode)) {
88d4d8fb18SAndreas Gohr                    $this->modes[$mode]->connectTo($cm);
89d4d8fb18SAndreas Gohr                }
90d4d8fb18SAndreas Gohr
91d4d8fb18SAndreas Gohr            }
92d4d8fb18SAndreas Gohr
93d4d8fb18SAndreas Gohr            $this->modes[$mode]->postConnect();
94d4d8fb18SAndreas Gohr        }
95d4d8fb18SAndreas Gohr
96d4d8fb18SAndreas Gohr        $this->connected = true;
97d4d8fb18SAndreas Gohr    }
98d4d8fb18SAndreas Gohr
99d4d8fb18SAndreas Gohr    /**
100d4d8fb18SAndreas Gohr     * Parses wiki syntax to instructions
101d4d8fb18SAndreas Gohr     *
102d4d8fb18SAndreas Gohr     * @param string $doc the wiki syntax text
103d4d8fb18SAndreas Gohr     * @return array instructions
104d4d8fb18SAndreas Gohr     */
105d4d8fb18SAndreas Gohr    public function parse($doc) {
106d4d8fb18SAndreas Gohr        $this->connectModes();
107d4d8fb18SAndreas Gohr        // Normalize CRs and pad doc
108d4d8fb18SAndreas Gohr        $doc = "\n" . str_replace("\r\n", "\n", $doc) . "\n";
109d4d8fb18SAndreas Gohr        $this->lexer->parse($doc);
110*1b008e87SMichael Große
111*1b008e87SMichael Große        if (!method_exists($this->handler, 'finalize')) {
112*1b008e87SMichael Große            /** @deprecated 2019-10 we have a legacy handler from a plugin, assume legacy _finalize exists */
113*1b008e87SMichael Große
114*1b008e87SMichael Große            \dokuwiki\Debug\DebugHelper::dbgCustomDeprecationEvent(
115*1b008e87SMichael Große                'finalize()',
116*1b008e87SMichael Große                get_class($this->handler) . '::_finalize()',
117*1b008e87SMichael Große                __METHOD__,
118*1b008e87SMichael Große                __FILE__,
119*1b008e87SMichael Große                __LINE__
120*1b008e87SMichael Große            );
121*1b008e87SMichael Große            $this->handler->_finalize();
122*1b008e87SMichael Große        } else {
123d4d8fb18SAndreas Gohr            $this->handler->finalize();
124*1b008e87SMichael Große        }
125d4d8fb18SAndreas Gohr        return $this->handler->calls;
126d4d8fb18SAndreas Gohr    }
127d4d8fb18SAndreas Gohr
128d4d8fb18SAndreas Gohr}
129