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