1<?php
2
3/**
4 * Hoa
5 *
6 *
7 * @license
8 *
9 * New BSD License
10 *
11 * Copyright © 2007-2017, Hoa community. All rights reserved.
12 *
13 * Redistribution and use in source and binary forms, with or without
14 * modification, are permitted provided that the following conditions are met:
15 *     * Redistributions of source code must retain the above copyright
16 *       notice, this list of conditions and the following disclaimer.
17 *     * Redistributions in binary form must reproduce the above copyright
18 *       notice, this list of conditions and the following disclaimer in the
19 *       documentation and/or other materials provided with the distribution.
20 *     * Neither the name of the Hoa nor the names of its contributors may be
21 *       used to endorse or promote products derived from this software without
22 *       specific prior written permission.
23 *
24 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
25 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS AND CONTRIBUTORS BE
28 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
29 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
30 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
31 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
32 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
33 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
34 * POSSIBILITY OF SUCH DAMAGE.
35 */
36
37namespace Hoa\Compiler\Llk\Sampler;
38
39use Hoa\Compiler;
40use Hoa\Consistency;
41use Hoa\Visitor;
42
43/**
44 * Class \Hoa\Compiler\Llk\Sampler.
45 *
46 * Sampler parent.
47 *
48 * @copyright  Copyright © 2007-2017 Hoa community
49 * @license    New BSD License
50 */
51abstract class Sampler
52{
53    /**
54     * Compiler.
55     *
56     * @var \Hoa\Compiler\Llk\Parser
57     */
58    protected $_compiler         = null;
59
60    /**
61     * Tokens.
62     *
63     * @var array
64     */
65    protected $_tokens           = null;
66
67    /**
68     * All rules (from the compiler).
69     *
70     * @var array
71     */
72    protected $_rules            = null;
73
74    /**
75     * Token sampler.
76     *
77     * @var \Hoa\Visitor\Visit
78     */
79    protected $_tokenSampler     = null;
80
81    /**
82     * Root rule name.
83     *
84     * @var string
85     */
86    protected $_rootRuleName     = null;
87
88    /**
89     * Current token namespace.
90     *
91     * @var string
92     */
93    protected $_currentNamespace = 'default';
94
95    /**
96     * Skip tokens AST.
97     *
98     * @var array
99     */
100    protected $_skipTokenAST     = [];
101
102
103    /**
104     * Construct a generator.
105     *
106     * @param   \Hoa\Compiler\Llk\Parser  $compiler        Compiler/parser.
107     * @param   \Hoa\Visitor\Visit        $tokenSampler    Token sampler.
108     */
109    public function __construct(
110        Compiler\Llk\Parser $compiler,
111        Visitor\Visit       $tokenSampler
112    ) {
113        $this->_compiler     = $compiler;
114        $this->_tokens       = $compiler->getTokens();
115        $this->_rules        = $compiler->getRules();
116        $this->_tokenSampler = $tokenSampler;
117        $this->_rootRuleName = $compiler->getRootRule();
118
119        return;
120    }
121
122    /**
123     * Get compiler.
124     *
125     * @return  \Hoa\Compiler\Llk\Parser
126     */
127    public function getCompiler()
128    {
129        return $this->_compiler;
130    }
131
132    /**
133     * Get the AST of the current namespace skip token.
134     *
135     * @return  \Hoa\Compiler\Llk\TreeNode
136     */
137    protected function getSkipTokenAST()
138    {
139        if (!isset($this->_skipTokenAST[$this->_currentNamespace])) {
140            $token = new Compiler\Llk\Rule\Token(
141                -1,
142                'skip',
143                null,
144                -1
145            );
146
147            $token->setRepresentation(
148                $this->_tokens[$this->_currentNamespace]['skip']
149            );
150
151            $this->_skipTokenAST[$this->_currentNamespace] = $token->getAST();
152        }
153
154        return $this->_skipTokenAST[$this->_currentNamespace];
155    }
156
157    /**
158     * Complete a token (namespace and representation).
159     * It returns the next namespace.
160     *
161     * @param   \Hoa\Compiler\Llk\Rule\Token  $token    Token.
162     * @return  string
163     */
164    protected function completeToken(Compiler\Llk\Rule\Token $token)
165    {
166        if (null !== $token->getRepresentation()) {
167            return $this->_currentNamespace;
168        }
169
170        $name = $token->getTokenName();
171        $token->setNamespace($this->_currentNamespace);
172        $toNamespace = $this->_currentNamespace;
173
174        if (isset($this->_tokens[$this->_currentNamespace][$name])) {
175            $token->setRepresentation(
176                $this->_tokens[$this->_currentNamespace][$name]
177            );
178        } else {
179            foreach ($this->_tokens[$this->_currentNamespace] as $_name => $regex) {
180                if (false === strpos($_name, ':')) {
181                    continue;
182                }
183
184                list($_name, $toNamespace) = explode(':', $_name, 2);
185
186                if ($_name === $name) {
187                    break;
188                }
189            }
190
191            $token->setRepresentation($regex);
192        }
193
194        return $toNamespace;
195    }
196
197    /**
198     * Set current token namespace.
199     *
200     * @param   string  $namespace    Token namespace.
201     * @return  string
202     */
203    protected function setCurrentNamespace($namespace)
204    {
205        $old                     = $this->_currentNamespace;
206        $this->_currentNamespace = $namespace;
207
208        return $old;
209    }
210
211    /**
212     * Generate a token value.
213     * Complete and set next token namespace.
214     *
215     * @param   \Hoa\Compiler\Llk\Rule\Token  $token    Token.
216     * @return  string
217     */
218    protected function generateToken(Compiler\Llk\Rule\Token $token)
219    {
220        $toNamespace = $this->completeToken($token);
221        $this->setCurrentNamespace($toNamespace);
222
223        $out = $this->_tokenSampler->visit($token->getAST());
224
225        if (isset($this->_tokens[$this->_currentNamespace]['skip'])) {
226            $out .= $this->_tokenSampler->visit($this->getSkipTokenAST());
227        }
228
229        return $out;
230    }
231}
232
233/**
234 * Flex entity.
235 */
236Consistency::flexEntity('Hoa\Compiler\Llk\Sampler\Sampler');
237