1<?php
2/**
3 * PHP_ParserGenerator, a php 5 parser generator.
4 *
5 * This is a direct port of the Lemon parser generator, found at
6 * {@link http://www.hwaci.com/sw/lemon/}
7 *
8 * PHP version 5
9 *
10 * LICENSE:
11 *
12 * Copyright (c) 2006, Gregory Beaver <cellog@php.net>
13 * All rights reserved.
14 *
15 * Redistribution and use in source and binary forms, with or without
16 * modification, are permitted provided that the following conditions
17 * are met:
18 *
19 *     * Redistributions of source code must retain the above copyright
20 *       notice, this list of conditions and the following disclaimer.
21 *     * Redistributions in binary form must reproduce the above copyright
22 *       notice, this list of conditions and the following disclaimer in
23 *       the documentation and/or other materials provided with the distribution.
24 *     * Neither the name of the PHP_ParserGenerator nor the names of its
25 *       contributors may be used to endorse or promote products derived
26 *       from this software without specific prior written permission.
27 *
28 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
29 * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
30 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
31 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
32 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
33 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
34 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
35 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
36 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
37 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
38 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
39 *
40 * @category  PHP
41 * @package   PHP_ParserGenerator
42 * @author    Gregory Beaver <cellog@php.net>
43 * @copyright 2006 Gregory Beaver
44 * @license   http://www.opensource.org/licenses/bsd-license.php New BSD License
45 * @version   CVS: $Id: Action.php 302382 2010-08-17 06:08:09Z jespino $
46 * @link      http://pear.php.net/package/PHP_ParserGenerator
47 * @since     File available since Release 0.1.0
48 */
49
50/**
51 * Every shift or reduce operation is stored as one of the following objects.
52 *
53 * @category  PHP
54 * @package   PHP_ParserGenerator
55 * @author    Gregory Beaver <cellog@php.net>
56 * @copyright 2006 Gregory Beaver
57 * @license   http://www.opensource.org/licenses/bsd-license.php New BSD License
58 * @version   Release: @package_version@
59 * @link      http://pear.php.net/package/PHP_ParserGenerator
60 * @since     Class available since Release 0.1.0
61 */
62class PHP_ParserGenerator_Action
63{
64    const SHIFT = 1,
65    ACCEPT = 2,
66    REDUCE = 3,
67    ERROR = 4,
68    /**
69           * Was a reduce, but part of a conflict
70           */
71    CONFLICT = 5,
72    /**
73           * Was a shift.  Precedence resolved conflict
74           */
75    SH_RESOLVED = 6,
76    /**
77           * Was a reduce.  Precedence resolved conflict
78           */
79    RD_RESOLVED = 7,
80    /**
81           * Deleted by compression
82           * @see PHP_ParserGenerator::CompressTables()
83           */
84    NOT_USED = 8;
85    /**
86     * The look-ahead symbol that triggers this action
87     * @var PHP_ParserGenerator_Symbol
88     */
89    public $sp;       /* The look-ahead symbol */
90    /**
91     * This defines the kind of action, and must be one
92     * of the class constants.
93     *
94     * - {@link PHP_ParserGenerator_Action::SHIFT}
95     * - {@link PHP_ParserGenerator_Action::ACCEPT}
96     * - {@link PHP_ParserGenerator_Action::REDUCE}
97     * - {@link PHP_ParserGenerator_Action::ERROR}
98     * - {@link PHP_ParserGenerator_Action::CONFLICT}
99     * - {@link PHP_ParserGenerator_Action::SH_RESOLVED}
100     * - {@link PHP_ParserGenerator_Action:: RD_RESOLVED}
101     * - {@link PHP_ParserGenerator_Action::NOT_USED}
102     */
103    public $type;
104    /**
105     * The new state, if this is a shift,
106     * the parser rule index, if this is a reduce.
107     *
108     * @var PHP_ParserGenerator_State|PHP_ParserGenerator_Rule
109     */
110    public $x;
111    /**
112     * The next action for this state.
113     * @var PHP_ParserGenerator_Action
114     */
115    public $next;
116
117    /**
118     * Compare two actions
119     *
120     * This is used by {@link Action_sort()} to compare actions
121     */
122    static function actioncmp(PHP_ParserGenerator_Action $ap1, PHP_ParserGenerator_Action $ap2)
123    {
124        $rc = $ap1->sp->index - $ap2->sp->index;
125        if ($rc === 0) {
126            $rc = $ap1->type - $ap2->type;
127        }
128        if ($rc === 0) {
129            if ($ap1->type == self::SHIFT) {
130                if ($ap1->x->statenum != $ap2->x->statenum) {
131                    throw new Exception('Shift conflict: ' . $ap1->sp->name .
132                        ' shifts both to state ' . $ap1->x->statenum . ' (rule ' .
133                        $ap1->x->cfp->rp->lhs->name . ' on line ' .
134                        $ap1->x->cfp->rp->ruleline . ') and to state ' .
135                        $ap2->x->statenum . ' (rule ' .
136                        $ap2->x->cfp->rp->lhs->name . ' on line ' .
137                        $ap2->x->cfp->rp->ruleline . ')');
138                }
139            }
140            if ($ap1->type != self::REDUCE
141                && $ap1->type != self::RD_RESOLVED
142                && $ap1->type != self::CONFLICT
143            ) {
144                throw new Exception('action has not been processed: ' .
145                $ap1->sp->name . ' on line ' . $ap1->x->cfp->rp->ruleline .
146                ', rule ' . $ap1->x->cfp->rp->lhs->name);
147            }
148            if ($ap2->type != self::REDUCE
149                && $ap2->type != self::RD_RESOLVED
150                && $ap2->type != self::CONFLICT
151            ) {
152                throw new Exception('action has not been processed: ' .
153                $ap2->sp->name . ' on line ' . $ap2->x->cfp->rp->ruleline .
154                ', rule ' . $ap2->x->cfp->rp->lhs->name);
155            }
156            $rc = $ap1->x->index - $ap2->x->index;
157        }
158        return $rc;
159    }
160
161    function display($processed = false)
162    {
163        $map = array(
164            self::ACCEPT => 'ACCEPT',
165            self::CONFLICT => 'CONFLICT',
166            self::REDUCE => 'REDUCE',
167            self::SHIFT => 'SHIFT'
168        );
169        echo $map[$this->type] . ' for ' . $this->sp->name;
170        if ($this->type == self::REDUCE) {
171            echo ' - rule ' . $this->x->lhs->name . "\n";
172        } elseif ($this->type == self::SHIFT) {
173            echo ' - state ' . $this->x->statenum . ', basis ' . $this->x->cfp->rp->lhs->name . "\n";
174        } else {
175            echo "\n";
176        }
177    }
178
179    /**
180     * create linked list of PHP_ParserGenerator_Actions
181     *
182     * @param PHP_ParserGenerator_Action|null                    $app
183     * @param int                                                $type one of the class constants from PHP_ParserGenerator_Action
184     * @param PHP_ParserGenerator_Symbol                         $sp
185     * @param PHP_ParserGenerator_State|PHP_ParserGenerator_Rule $arg
186     */
187    static function Action_add(&$app, $type, PHP_ParserGenerator_Symbol $sp, $arg)
188    {
189        $new = new PHP_ParserGenerator_Action;
190        $new->next = $app;
191        $app = $new;
192        $new->type = $type;
193        $new->sp = $sp;
194        $new->x = $arg;
195        echo ' Adding ';
196        $new->display();
197    }
198
199    /**
200     * Sort parser actions
201     *
202     * @param PHP_ParserGenerator_Action $ap a parser action
203     *
204     * @see PHP_ParserGenerator_Data::FindActions()
205     *
206     * @return PHP_ParserGenerator_Action
207     */
208    static function Action_sort(PHP_ParserGenerator_Action $ap)
209    {
210        $ap = PHP_ParserGenerator::msort($ap, 'next', array('PHP_ParserGenerator_Action', 'actioncmp'));
211        return $ap;
212    }
213
214    /**
215     * Print an action to the given file descriptor.  Return FALSE if
216     * nothing was actually printed.
217     *
218     * @param resource $fp     File descriptor to print on
219     * @param integer  $indent Number of indents
220     *
221     * @see PHP_ParserGenerator_Data::ReportOutput()
222     *
223     * @return int|false
224     */
225    function PrintAction($fp, $indent)
226    {
227        if (!$fp) {
228            $fp = STDOUT;
229        }
230        $result = 1;
231        switch ($this->type)
232        {
233        case self::SHIFT:
234            fprintf($fp, "%${indent}s shift  %d", $this->sp->name, $this->x->statenum);
235            break;
236        case self::REDUCE:
237            fprintf($fp, "%${indent}s reduce %d", $this->sp->name, $this->x->index);
238            break;
239        case self::ACCEPT:
240            fprintf($fp, "%${indent}s accept", $this->sp->name);
241            break;
242        case self::ERROR:
243            fprintf($fp, "%${indent}s error", $this->sp->name);
244            break;
245        case self::CONFLICT:
246            fprintf($fp, "%${indent}s reduce %-3d ** Parsing conflict **", $this->sp->name, $this->x->index);
247            break;
248        case self::SH_RESOLVED:
249        case self::RD_RESOLVED:
250        case self::NOT_USED:
251            $result = 0;
252            break;
253        }
254        return $result;
255    }
256}
257?>
258