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