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\Bin;
38
39use Hoa\Compiler;
40use Hoa\Consistency;
41use Hoa\Console;
42use Hoa\File;
43
44/**
45 * Class Hoa\Compiler\Bin\Pp.
46 *
47 * Play with PP.
48 *
49 * @copyright  Copyright © 2007-2017 Hoa community
50 * @license    New BSD License
51 */
52class Pp extends Console\Dispatcher\Kit
53{
54    /**
55     * Options description.
56     *
57     * @var array
58     */
59    protected $options = [
60        ['visitor',        Console\GetOption::REQUIRED_ARGUMENT, 'v'],
61        ['visitor-class',  Console\GetOption::REQUIRED_ARGUMENT, 'c'],
62        ['token-sequence', Console\GetOption::NO_ARGUMENT,       's'],
63        ['trace',          Console\GetOption::NO_ARGUMENT,       't'],
64        ['help',           Console\GetOption::NO_ARGUMENT,       'h'],
65        ['help',           Console\GetOption::NO_ARGUMENT,       '?']
66    ];
67
68
69
70    /**
71     * The entry method.
72     *
73     * @return  int
74     */
75    public function main()
76    {
77        $visitor       = null;
78        $tokenSequence = false;
79        $trace         = false;
80
81        while (false !== $c = $this->getOption($v)) {
82            switch ($c) {
83                case 'v':
84                    switch (strtolower($v)) {
85                        case 'dump':
86                            $visitor = 'Hoa\Compiler\Visitor\Dump';
87
88                            break;
89
90                        default:
91                            return $this->usage();
92                    }
93
94                    break;
95
96                case 'c':
97                    $visitor = str_replace('.', '\\', $v);
98
99                    break;
100
101                case 's':
102                    $tokenSequence = true;
103
104                    break;
105
106                case 't':
107                    $trace = true;
108
109                    break;
110
111                case '__ambiguous':
112                    $this->resolveOptionAmbiguity($v);
113
114                    break;
115
116                case 'h':
117                case '?':
118                default:
119                    return $this->usage();
120            }
121        }
122
123        $this->parser->listInputs($grammar, $language);
124
125        if (empty($grammar) || (empty($language) && '0' !== $language)) {
126            return $this->usage();
127        }
128
129        $compiler = Compiler\Llk::load(new File\Read($grammar));
130        $stream   = new File\Read($language);
131        $data     = $stream->readAll();
132
133        try {
134            $ast = $compiler->parse($data);
135        } catch (Compiler\Exception $e) {
136            if (true === $tokenSequence) {
137                $this->printTokenSequence($compiler, $data);
138                echo "\n\n";
139            }
140
141            throw $e;
142
143            return 1;
144        }
145
146        if (true === $tokenSequence) {
147            $this->printTokenSequence($compiler, $data);
148            echo "\n\n";
149        }
150
151        if (true === $trace) {
152            $this->printTrace($compiler);
153            echo "\n\n";
154        }
155
156        if (null !== $visitor) {
157            $visitor = Consistency\Autoloader::dnew($visitor);
158            echo $visitor->visit($ast);
159        }
160
161        return;
162    }
163
164    /**
165     * Print trace.
166     *
167     * @param   \Hoa\Compiler\Llk\Parser  $compiler    Compiler.
168     * @return  void
169     */
170    protected function printTrace(Compiler\Llk\Parser $compiler)
171    {
172        $i = 0;
173
174        foreach ($compiler->getTrace() as $element) {
175            if ($element instanceof Compiler\Llk\Rule\Entry) {
176                $ruleName = $element->getRule();
177                $rule     = $compiler->getRule($ruleName);
178
179                echo str_repeat('>  ', ++$i), 'enter ', $ruleName;
180
181                if (null !== $id = $rule->getNodeId()) {
182                    echo ' (', $id, ')';
183                }
184
185                echo "\n";
186            } elseif ($element instanceof Compiler\Llk\Rule\Token) {
187                echo
188                    str_repeat('   ', $i + 1),
189                    'token ', $element->getTokenName(),
190                    ', consumed ', $element->getValue(), "\n";
191            } else {
192                echo
193                    str_repeat('<  ', $i--),
194                   'ekzit ', $element->getRule(), "\n";
195            }
196        }
197
198        return;
199    }
200
201    /**
202     * Print token sequence.
203     *
204     * @param   \Hoa\Compiler\Llk\Parser  $compiler    Compiler.
205     * @param   string                    $data        Data to lex.
206     * @return  void
207     */
208    protected function printTokenSequence(Compiler\Llk\Parser $compiler, $data)
209    {
210        $lexer    = new Compiler\Llk\Lexer();
211        $sequence = $lexer->lexMe($data, $compiler->getTokens());
212        $format   = '%' . (strlen((string) count($sequence)) + 1) . 's  ' .
213                    '%-13s %-20s  %s  %6s' . "\n";
214
215        $header = sprintf(
216            $format,
217            '#',
218            'namespace',
219            'token name',
220            'token value                   ',
221            'offset'
222        );
223
224        echo $header, str_repeat('-', strlen($header)), "\n";
225
226        foreach ($sequence as $i => $token) {
227            printf(
228                $format,
229                $i,
230                $token['namespace'],
231                $token['token'],
232                30 < $token['length']
233                    ? mb_substr($token['value'], 0, 29) . '…'
234                    : 'EOF' === $token['token']
235                        ? str_repeat(' ', 30)
236                        : $token['value'] .
237                          str_repeat(' ', 30 - $token['length']),
238                $token['offset']
239            );
240        }
241
242        return;
243    }
244
245    /**
246     * The command usage.
247     *
248     * @return  int
249     */
250    public function usage()
251    {
252        echo
253            'Usage   : compiler:pp <options> [grammar.pp] [language]', "\n",
254            'Options :', "\n",
255            $this->makeUsageOptionsList([
256                'v'    => 'Visitor name (only “dump” is supported).',
257                'c'    => 'Visitor classname (using . instead of \ works).',
258                's'    => 'Print token sequence.',
259                't'    => 'Print trace.',
260                'help' => 'This help.'
261            ]), "\n";
262
263        return;
264    }
265}
266
267__halt_compiler();
268Compile and visit languages with grammars.
269