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: Config.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/** A configuration is a production rule of the grammar together with
51 * a mark (dot) showing how much of that rule has been processed so far.
52 *
53 * Configurations also contain a follow-set which is a list of terminal
54 * symbols which are allowed to immediately follow the end of the rule.
55 * Every configuration is recorded as an instance of the following class.
56 *
57 * @category  PHP
58 * @package   PHP_ParserGenerator
59 * @author    Gregory Beaver <cellog@php.net>
60 * @copyright 2006 Gregory Beaver
61 * @license   http://www.opensource.org/licenses/bsd-license.php New BSD License
62 * @version   Release: @package_version@
63 * @link      http://pear.php.net/package/PHP_ParserGenerator
64 * @since     Class available since Release 0.1.0
65 */
66class PHP_ParserGenerator_Config
67{
68    const COMPLETE = 1;
69    const INCOMPLETE = 2;
70    /**
71     * The parser rule upon with the configuration is based.
72     *
73     * A parser rule is something like:
74     * <pre>
75     * blah ::= FOO bar.
76     * </pre>
77     * @var PHP_ParserGenerator_Rule
78     */
79    public $rp;
80    /**
81     * The parse point.
82     *
83     * This is the index into the right-hand side of a rule that is
84     * represented by this configuration.  In other words, possible
85     * dots for this rule:
86     *
87     * <pre>
88     * blah ::= FOO bar.
89     * </pre>
90     *
91     * are (represented by "[here]"):
92     *
93     * <pre>
94     * blah ::= [here] FOO bar.
95     * blah ::= FOO [here] bar.
96     * blah ::= FOO bar [here].
97     * </pre>
98     * @var int
99     */
100    public $dot;
101    /**
102     * Follow-set for this configuration only
103     *
104     * This is the list of terminals and non-terminals that
105     * can follow this configuration.
106     * @var array
107     */
108    public $fws;
109    /**
110     * Follow-set forward propagation links.
111     * @var PHP_ParserGenerator_PropagationLink
112     */
113    public $fplp;
114    /**
115     * Follow-set backwards propagation links
116     * @var PHP_ParserGenerator_PropagationLink
117     */
118    public $bplp;
119    /**
120     * State that contains this configuration
121     * @var PHP_ParserGenerator_State
122     */
123    public $stp;
124  /* enum {
125    COMPLETE,              /* The status is used during followset and
126    INCOMPLETE             /*    shift computations
127  } */
128    /**
129     * Status during followset and shift computations.
130     *
131     * One of PHP_ParserGenerator_Config::COMPLETE or
132     * PHP_ParserGenerator_Config::INCOMPLETE.
133     * @var int
134     */
135    public $status;
136    /**
137     * Next configuration in the state.
138     *
139     * Index of next PHP_ParserGenerator_Config object.
140     * @var int
141     */
142    public $next;
143    /**
144     * Index of the next basis configuration PHP_ParserGenerator_Config object
145     * @var int
146     */
147    public $bp;
148
149    /**
150     * Top of the list of configurations for the current state.
151     * @var PHP_ParserGenerator_Config
152     */
153    static public $current;
154    /**
155     * Last on the list of configurations for the current state.
156     * @var PHP_ParserGenerator_Config
157     */
158    static public $currentend;
159
160    /**
161     * Top of the list of basis configurations for the current state.
162     * @var PHP_ParserGenerator_Config
163     */
164    static public $basis;
165    /**
166     * Last on the list of basis configurations for the current state.
167     * @var PHP_ParserGenerator_Config
168     */
169    static public $basisend;
170
171    /**
172     * Associative array representation of the linked list of configurations
173     * found in {@link $current}
174     *
175     * @var array
176     */
177    static public $x4a = array();
178
179    /**
180     * Return a pointer to a new configuration
181     * @return PHP_ParserGenerator_Config
182     */
183    private static function newconfig()
184    {
185        return new PHP_ParserGenerator_Config;
186    }
187
188    /**
189     * Display the current configuration for the .out file
190     *
191     * @param PHP_ParserGenerator_Config $cfp
192     * @see PHP_ParserGenerator_Data::ReportOutput()
193     */
194    static function Configshow(PHP_ParserGenerator_Config $cfp)
195    {
196        $fp = fopen('php://output', 'w');
197        while ($cfp) {
198            if ($cfp->dot == $cfp->rp->nrhs) {
199                $buf = sprintf('(%d)', $cfp->rp->index);
200                fprintf($fp, '    %5s ', $buf);
201            } else {
202                fwrite($fp,'          ');
203            }
204            $cfp->ConfigPrint($fp);
205            fwrite($fp, "\n");
206            if (0) {
207                //SetPrint(fp,cfp->fws,$this);
208                //PlinkPrint(fp,cfp->fplp,"To  ");
209                //PlinkPrint(fp,cfp->bplp,"From");
210            }
211            $cfp = $cfp->next;
212        }
213        fwrite($fp, "\n");
214        fclose($fp);
215    }
216
217    /**
218     * Initialize the configuration list builder for a new state.
219     */
220    static function Configlist_init()
221    {
222        self::$current = 0;
223        self::$currentend = &self::$current;
224        self::$basis = 0;
225        self::$basisend = &self::$basis;
226        self::$x4a = array();
227    }
228
229    /**
230     * Remove all data from the table.
231     *
232     * Pass each data to the function $f as it is removed if
233     * $f is a valid callback.
234     * @param callback|null
235     * @see Configtable_clear()
236     */
237    static function Configtable_reset($f)
238    {
239        self::$current = 0;
240        self::$currentend = &self::$current;
241        self::$basis = 0;
242        self::$basisend = &self::$basis;
243        self::Configtable_clear(0);
244    }
245
246    /**
247     * Remove all data from the associative array representation
248     * of configurations.
249     *
250     * Pass each data to the function $f as it is removed if
251     * $f is a valid callback.
252     * @param callback|null
253     */
254    static function Configtable_clear($f)
255    {
256        if (!count(self::$x4a)) {
257            return;
258        }
259        if ($f) {
260            for ($i = 0; $i < count(self::$x4a); $i++) {
261                call_user_func($f, self::$x4a[$i]->data);
262            }
263        }
264        self::$x4a = array();
265    }
266
267    /**
268     * Reset the configuration list builder for a new state.
269     * @see Configtable_clear()
270     */
271    static function Configlist_reset()
272    {
273        self::Configtable_clear(0);
274    }
275
276    /**
277     * Add another configuration to the configuration list for this parser state.
278     * @param PHP_ParserGenerator_Rule the rule
279     * @param int Index into the right-hand side of the rule where the dot goes
280     * @return PHP_ParserGenerator_Config
281     */
282    static function Configlist_add($rp, $dot)
283    {
284        $model = new PHP_ParserGenerator_Config;
285        $model->rp = $rp;
286        $model->dot = $dot;
287        $cfp = self::Configtable_find($model);
288        if ($cfp === 0) {
289            $cfp = self::newconfig();
290            $cfp->rp = $rp;
291            $cfp->dot = $dot;
292            $cfp->fws = array();
293            $cfp->stp = 0;
294            $cfp->fplp = $cfp->bplp = 0;
295            $cfp->next = 0;
296            $cfp->bp = 0;
297            self::$currentend = $cfp;
298            self::$currentend = &$cfp->next;
299            self::Configtable_insert($cfp);
300        }
301        return $cfp;
302    }
303
304    /**
305     * Add a basis configuration to the configuration list for this parser state.
306     *
307     * Basis configurations are the root for a configuration.  This method also
308     * inserts the configuration into the regular list of configurations for this
309     * reason.
310     * @param PHP_ParserGenerator_Rule the rule
311     * @param int Index into the right-hand side of the rule where the dot goes
312     * @return PHP_ParserGenerator_Config
313     */
314    static function Configlist_addbasis($rp, $dot)
315    {
316        $model = new PHP_ParserGenerator_Config;
317        $model->rp = $rp;
318        $model->dot = $dot;
319        $cfp = self::Configtable_find($model);
320        if ($cfp === 0) {
321            $cfp = self::newconfig();
322            $cfp->rp = $rp;
323            $cfp->dot = $dot;
324            $cfp->fws = array();
325            $cfp->stp = 0;
326            $cfp->fplp = $cfp->bplp = 0;
327            $cfp->next = 0;
328            $cfp->bp = 0;
329            self::$currentend = $cfp;
330            self::$currentend = &$cfp->next;
331            self::$basisend = $cfp;
332            self::$basisend = &$cfp->bp;
333            self::Configtable_insert($cfp);
334        }
335        return $cfp;
336    }
337
338    /**
339     * Compute the closure of the configuration list.
340     *
341     * This calculates all of the possible continuations of
342     * each configuration, ensuring that each state accounts
343     * for every configuration that could arrive at that state.
344     */
345    static function Configlist_closure(PHP_ParserGenerator_Data $lemp)
346    {
347        for ($cfp = self::$current; $cfp; $cfp = $cfp->next) {
348            $rp = $cfp->rp;
349            $dot = $cfp->dot;
350            if ($dot >= $rp->nrhs) {
351                continue;
352            }
353            $sp = $rp->rhs[$dot];
354            if ($sp->type == PHP_ParserGenerator_Symbol::NONTERMINAL) {
355                if ($sp->rule === 0 && $sp !== $lemp->errsym) {
356                    PHP_ParserGenerator::ErrorMsg($lemp->filename, $rp->line,
357                        "Nonterminal \"%s\" has no rules.", $sp->name);
358                    $lemp->errorcnt++;
359                }
360                for ($newrp = $sp->rule; $newrp; $newrp = $newrp->nextlhs) {
361                    $newcfp = self::Configlist_add($newrp, 0);
362                    for ($i = $dot + 1; $i < $rp->nrhs; $i++) {
363                        $xsp = $rp->rhs[$i];
364                        if ($xsp->type == PHP_ParserGenerator_Symbol::TERMINAL) {
365                            $newcfp->fws[$xsp->index] = 1;
366                            break;
367                        } elseif ($xsp->type == PHP_ParserGenerator_Symbol::MULTITERMINAL) {
368                            for ($k = 0; $k < $xsp->nsubsym; $k++) {
369                                $newcfp->fws[$xsp->subsym[$k]->index] = 1;
370                            }
371                            break;
372                        } else {
373                            $a = array_diff_key($xsp->firstset, $newcfp->fws);
374                            $newcfp->fws += $a;
375                            if ($xsp->lambda === false) {
376                                break;
377                            }
378                        }
379                    }
380                    if ($i == $rp->nrhs) {
381                        PHP_ParserGenerator_PropagationLink::Plink_add($cfp->fplp, $newcfp);
382                    }
383                }
384            }
385        }
386    }
387
388    /**
389     * Sort the configuration list
390     * @uses Configcmp()
391     */
392    static function Configlist_sort()
393    {
394        $a = 0;
395        //self::Configshow(self::$current);
396        self::$current = PHP_ParserGenerator::msort(self::$current,'next', array('PHP_ParserGenerator_Config', 'Configcmp'));
397        //self::Configshow(self::$current);
398        self::$currentend = &$a;
399        self::$currentend = 0;
400    }
401
402    /**
403     * Sort the configuration list
404     * @uses Configcmp
405     */
406    static function Configlist_sortbasis()
407    {
408        $a = 0;
409        self::$basis = PHP_ParserGenerator::msort(self::$current,'bp', array('PHP_ParserGenerator_Config', 'Configcmp'));
410        self::$basisend = &$a;
411        self::$basisend = 0;
412    }
413
414    /**
415     * Return a pointer to the head of the configuration list and
416     * reset the list
417     * @see $current
418     * @return PHP_ParserGenerator_Config
419     */
420    static function Configlist_return()
421    {
422        $old = self::$current;
423        self::$current = 0;
424        self::$currentend = &self::$current;
425        return $old;
426    }
427
428    /**
429     * Return a pointer to the head of the basis list and
430     * reset the list
431     * @see $basis
432     * @return PHP_ParserGenerator_Config
433     */
434    static function Configlist_basis()
435    {
436        $old = self::$basis;
437        self::$basis = 0;
438        self::$basisend = &self::$basis;
439        return $old;
440    }
441
442    /**
443     * Free all elements of the given configuration list
444     * @param PHP_ParserGenerator_Config
445     */
446    static function Configlist_eat($cfp)
447    {
448        for (; $cfp; $cfp = $nextcfp) {
449            $nextcfp = $cfp->next;
450            if ($cfp->fplp !=0) {
451                throw new Exception('fplp of configuration non-zero?');
452            }
453            if ($cfp->bplp !=0) {
454                throw new Exception('bplp of configuration non-zero?');
455            }
456            if ($cfp->fws) {
457                $cfp->fws = array();
458            }
459        }
460    }
461
462    /**
463     * Compare two configurations for sorting purposes.
464     *
465     * Configurations based on higher precedence rules
466     * (those earlier in the file) are chosen first.  Two
467     * configurations that are the same rule are sorted by
468     * dot (see {@link $dot}), and those configurations
469     * with a dot closer to the left-hand side are chosen first.
470     * @param unknown_type $a
471     * @param unknown_type $b
472     * @return unknown
473     */
474    static function Configcmp($a, $b)
475    {
476        $x = $a->rp->index - $b->rp->index;
477        if (!$x) {
478            $x = $a->dot - $b->dot;
479        }
480        return $x;
481    }
482
483    /**
484     * Print out information on this configuration.
485     *
486     * @param resource $fp
487     * @see PHP_ParserGenerator_Data::ReportOutput()
488     */
489    function ConfigPrint($fp)
490    {
491        $rp = $this->rp;
492        fprintf($fp, "%s ::=", $rp->lhs->name);
493        for ($i = 0; $i <= $rp->nrhs; $i++) {
494            if ($i === $this->dot) {
495                fwrite($fp,' *');
496            }
497            if ($i === $rp->nrhs) {
498                break;
499            }
500            $sp = $rp->rhs[$i];
501            fprintf($fp,' %s', $sp->name);
502            if ($sp->type == PHP_ParserGenerator_Symbol::MULTITERMINAL) {
503                for ($j = 1; $j < $sp->nsubsym; $j++) {
504                    fprintf($fp, '|%s', $sp->subsym[$j]->name);
505                }
506            }
507        }
508    }
509
510    /**
511     * Hash a configuration for the associative array {@link $x4a}
512     */
513    private static function confighash(PHP_ParserGenerator_Config $a)
514    {
515        $h = 0;
516        $h = $h * 571 + $a->rp->index * 37 + $a->dot;
517        return $h;
518    }
519
520    /**
521     * Insert a new record into the array.  Return TRUE if successful.
522     * Prior data with the same key is NOT overwritten
523     */
524    static function Configtable_insert(PHP_ParserGenerator_Config $data)
525    {
526        $h = self::confighash($data);
527        if (isset(self::$x4a[$h])) {
528            $np = self::$x4a[$h];
529        } else {
530            $np = 0;
531        }
532        while ($np) {
533            if (self::Configcmp($np->data, $data) == 0) {
534                /* An existing entry with the same key is found. */
535                /* Fail because overwrite is not allows. */
536                return 0;
537            }
538            $np = $np->next;
539        }
540        /* Insert the new data */
541        $np = array('data' => $data, 'next' => 0, 'from' => 0);
542        $np = new PHP_ParserGenerator_StateNode;
543        $np->data = $data;
544        if (isset(self::$x4a[$h])) {
545            self::$x4a[$h]->from = $np->next;
546            $np->next = self::$x4a[$h];
547        }
548        $np->from = $np;
549        self::$x4a[$h] = $np;
550        return 1;
551    }
552
553    /**
554     * Return a pointer to data assigned to the given key.  Return NULL
555     * if no such key.
556     * @return PHP_ParserGenerator_Config|0
557     */
558    static function Configtable_find(PHP_ParserGenerator_Config $key)
559    {
560        $h = self::confighash($key);
561        if (!isset(self::$x4a[$h])) {
562            return 0;
563        }
564        $np = self::$x4a[$h];
565        while ($np) {
566            if (self::Configcmp($np->data, $key) == 0) {
567                break;
568            }
569            $np = $np->next;
570        }
571        return $np ? $np->data : 0;
572    }
573}
574?>
575