lime/lime.php

1314 lines
29 KiB
PHP
Raw Normal View History

#!/usr/bin/php -q
2011-12-28 01:23:38 +04:00
<?php
/*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Library General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
2011-12-28 04:00:14 +04:00
define('LIME_DIR', __DIR__);
2011-12-28 01:23:38 +04:00
2011-12-28 04:00:14 +04:00
function emit($str) {
fputs(STDERR, $str . PHP_EOL);
}
class Bug extends Exception {
}
function bug($gripe = 'Bug found.') {
throw new Bug($gripe);
}
function bug_if($fallacy, $gripe = 'Bug found.') {
if ($fallacy) {
throw new Bug($gripe);
}
}
function bug_unless($assertion, $gripe = 'Bug found.') {
if (!$assertion) {
throw new Bug($gripe);
}
}
require LIME_DIR . '/parse_engine.php';
require LIME_DIR . '/set.so.php';
require LIME_DIR . '/flex_token_stream.php';
function lime_token_reference($pos) {
return '$tokens[' . $pos . ']';
}
function lime_token_reference_callback($foo) {
if ($foo[1] === '$') {
// always
return '$result';
}
return lime_token_reference($foo[1] - 1);
}
function lime_export($var) {
if (is_array($var)) {
$i = is_indexed($var);
$out = array();
foreach($var as $k => $v) {
$out[] = (!$i ? lime_export($k).' => ' : '') . lime_export($v);
}
$result = 'array(' . PHP_EOL . preg_replace('~^~m', "\t", implode(',' . PHP_EOL, $out)) . PHP_EOL . ')';
} elseif (is_int($var) || is_float($var)) {
$result = (string)$var;
} elseif (is_string($var)) {
$opt1 = "'" . str_replace(array('\\', "'"), array('\\\\', "\'"), $var) . "'";
$opt2 = $opt1;
if (strpos($var, '$') === false) {
$opt2 = '"' . str_replace(array('\\', '"'), array('\\\\', '\"'), $var) . '"';
}
if (strlen($opt1) <= strlen($opt2)) {
$result = $opt1;
} else {
$result = $opt2;
}
} elseif (is_bool($var)) {
$result = $var ? 'true' : 'false';
} else {
bug('Wrong type: ' . gettype($var));
}
return $result;
}
function is_indexed(array $array) {
$i = 0;
foreach($array as $k => $v) {
if ($k !== $i++) {
return false;
}
}
2011-12-28 01:23:38 +04:00
2011-12-28 04:00:14 +04:00
return true;
}
2011-12-28 01:23:38 +04:00
2011-12-28 04:00:14 +04:00
function unindent($text) {
if (preg_match('{\A[\r\n]*([ \t]+)[^\r\n]*+(?:[\r\n]++(?>\1[^\r\n]*+(?:[\r\n]+|\z)|[\r\n]+)+)?\z}', rtrim($text), $match)) {
$text = preg_replace('{^' . $match[1] . '}m', '', $text);
}
return $text;
}
2011-12-28 01:23:38 +04:00
class cf_action {
2011-12-28 04:00:14 +04:00
protected $code;
public function __construct($code) {
$this->code = $code;
}
2011-12-28 01:23:38 +04:00
}
2011-12-28 04:00:14 +04:00
/**
* Base class for parse table instructions. The main idea is to make the
* subclasses responsible for conflict resolution among themselves. It also
* forms a sort of interface to the parse table.
*/
abstract class step {
public $sym;
public function __construct(sym $sym) {
2011-12-28 01:23:38 +04:00
$this->sym = $sym;
}
2011-12-28 04:00:14 +04:00
public function glyph() {
return $this->sym->name;
}
public function sane() {
return true;
}
abstract public function instruction();
abstract public function decide($that);
2011-12-28 01:23:38 +04:00
}
2011-12-28 04:00:14 +04:00
2011-12-28 01:23:38 +04:00
class error extends step {
2011-12-28 04:00:14 +04:00
public function sane() {
return false;
}
public function instruction() {
bug('This should not happen.');
}
public function decide($that) {
// An error shall remain one
return $this;
}
2011-12-28 01:23:38 +04:00
}
2011-12-28 04:00:14 +04:00
2011-12-28 01:23:38 +04:00
class shift extends step {
2011-12-28 04:00:14 +04:00
public $q;
public function __construct(sym $sym, $q) {
2011-12-28 01:23:38 +04:00
parent::__construct($sym);
2011-12-28 04:00:14 +04:00
2011-12-28 01:23:38 +04:00
$this->q = $q;
}
2011-12-28 04:00:14 +04:00
public function instruction() {
return 's ' . $this->q;
}
public function decide($that) {
// shift-shift conflicts are impossible.
// shift-accept conflicts are a bug.
// so we can infer:
2011-12-28 01:23:38 +04:00
bug_unless($that instanceof reduce);
2011-12-28 04:00:14 +04:00
// That being said, the resolution is a matter of precedence.
2011-12-28 01:23:38 +04:00
$shift_prec = $this->sym->right_prec;
$reduce_prec = $that->rule->prec;
2011-12-28 04:00:14 +04:00
// If we don't have defined precedence levels for both options,
// then we default to shifting:
if (!($shift_prec and $reduce_prec)) {
return $this;
}
// Otherwise, use the step with higher precedence.
if ($shift_prec > $reduce_prec) {
return $this;
}
if ($reduce_prec > $shift_prec) {
return $that;
}
// The "nonassoc" works by giving equal precedence to both options,
// which means to put an error instruction in the parse table.
2011-12-28 01:23:38 +04:00
return new error($this->sym);
}
}
2011-12-28 04:00:14 +04:00
2011-12-28 01:23:38 +04:00
class reduce extends step {
2011-12-28 04:00:14 +04:00
public function __construct($sym, rule $rule) {
2011-12-28 01:23:38 +04:00
parent::__construct($sym);
$this->rule = $rule;
}
2011-12-28 04:00:14 +04:00
public function instruction() {
return 'r ' . $this->rule->id;
}
2011-12-28 01:23:38 +04:00
function decide($that) {
2011-12-28 04:00:14 +04:00
// This means that the input grammar has a reduce-reduce conflict.
// Such things are considered an error in the input.
2011-12-28 01:23:38 +04:00
throw new RRC($this, $that);
2011-12-28 04:00:14 +04:00
// BISON would go with the first encountered reduce thus:
// return $this;
2011-12-28 01:23:38 +04:00
}
}
2011-12-28 04:00:14 +04:00
2011-12-28 01:23:38 +04:00
class accept extends step {
2011-12-28 04:00:14 +04:00
public function __construct(sym $sym) {
parent::__construct($sym);
}
public function instruction() {
return 'a ' . $this->sym->name;
}
public function decide($that) {
return $this;
}
2011-12-28 01:23:38 +04:00
}
2011-12-28 04:00:14 +04:00
2011-12-28 01:23:38 +04:00
class RRC extends Exception {
2011-12-28 04:00:14 +04:00
public function __construct($a, $b) {
parent::__construct('Reduce-Reduce Conflict');
2011-12-28 01:23:38 +04:00
$this->a = $a;
$this->b = $b;
}
2011-12-28 04:00:14 +04:00
2011-12-28 01:23:38 +04:00
function make_noise() {
emit(sprintf(
2011-12-28 04:00:14 +04:00
'Reduce-Reduce Conflict:' . PHP_EOL . '%s' . PHP_EOL . '%s' . PHP_EOL . 'Lookahead is (%s)',
2011-12-28 01:23:38 +04:00
$this->a->rule->text(),
$this->b->rule->text(),
$this->a->glyph()
));
}
}
2011-12-28 04:00:14 +04:00
2011-12-28 01:23:38 +04:00
class state {
2011-12-28 04:00:14 +04:00
public function __construct($id, $key, $close) {
2011-12-28 01:23:38 +04:00
$this->id = $id;
$this->key = $key;
2011-12-28 04:00:14 +04:00
$this->close = $close; // config key -> object
2011-12-28 01:23:38 +04:00
ksort($this->close);
$this->action = array();
}
2011-12-28 04:00:14 +04:00
public function dump() {
echo ' * ' . $this->id . ' / ' . $this->key . PHP_EOL;
foreach ($this->close as $config) {
$config->dump();
}
2011-12-28 01:23:38 +04:00
}
2011-12-28 04:00:14 +04:00
public function add_shift(sym $sym, $state) {
2011-12-28 01:23:38 +04:00
$this->add_instruction(new shift($sym, $state->id));
}
2011-12-28 04:00:14 +04:00
public function add_reduce(sym $sym, $rule) {
2011-12-28 01:23:38 +04:00
$this->add_instruction(new reduce($sym, $rule));
}
2011-12-28 04:00:14 +04:00
public function add_accept(sym $sym) {
2011-12-28 01:23:38 +04:00
$this->add_instruction(new accept($sym));
}
2011-12-28 04:00:14 +04:00
public function add_instruction(step $step) {
2011-12-28 01:23:38 +04:00
$this->action[] = $step;
}
2011-12-28 04:00:14 +04:00
2011-12-28 01:23:38 +04:00
function find_reductions($lime) {
2011-12-28 04:00:14 +04:00
// rightmost configurations followset yields reduce.
2011-12-28 01:23:38 +04:00
foreach($this->close as $c) {
if ($c->rightmost) {
2011-12-28 04:00:14 +04:00
foreach ($c->follow->all() as $glyph) {
$this->add_reduce($lime->sym($glyph), $c->rule);
}
2011-12-28 01:23:38 +04:00
}
}
}
2011-12-28 04:00:14 +04:00
2011-12-28 01:23:38 +04:00
function resolve_conflicts() {
2011-12-28 04:00:14 +04:00
// For each possible lookahead, find one (and only one) step to take.
2011-12-28 01:23:38 +04:00
$table = array();
foreach ($this->action as $step) {
$glyph = $step->glyph();
if (isset($table[$glyph])) {
2011-12-28 04:00:14 +04:00
// There's a conflict. The shifts all came first, which
// simplifies the coding for the step->decide() methods.
2011-12-28 01:23:38 +04:00
try {
$table[$glyph] = $table[$glyph]->decide($step);
} catch (RRC $e) {
2011-12-28 04:00:14 +04:00
emit('State ' . $this->id . ':');
2011-12-28 01:23:38 +04:00
$e->make_noise();
}
} else {
2011-12-28 04:00:14 +04:00
// This glyph is yet unprocessed, so the step at hand is
// our best current guess at what the grammar indicates.
2011-12-28 01:23:38 +04:00
$table[$glyph] = $step;
}
}
2011-12-28 04:00:14 +04:00
// Now that we have the correct steps chosen, this routine is oddly
// also responsible for turning that table into the form that will
// eventually be passed to the parse engine. (So FIXME?)
2011-12-28 01:23:38 +04:00
$out = array();
foreach ($table as $glyph => $step) {
2011-12-28 04:00:14 +04:00
if ($step->sane()) {
$out[$glyph] = $step->instruction();
}
2011-12-28 01:23:38 +04:00
}
2011-12-28 04:00:14 +04:00
2011-12-28 01:23:38 +04:00
return $out;
}
2011-12-28 04:00:14 +04:00
2011-12-28 01:23:38 +04:00
function segment_config() {
2011-12-28 04:00:14 +04:00
// Filter $this->close into categories based on the symbol_after_the_dot.
2011-12-28 01:23:38 +04:00
$f = array();
2011-12-28 04:00:14 +04:00
2011-12-28 01:23:38 +04:00
foreach ($this->close as $c) {
$p = $c->symbol_after_the_dot;
2011-12-28 04:00:14 +04:00
if (!$p) {
continue;
}
2011-12-28 01:23:38 +04:00
$f[$p->name][] = $c;
}
2011-12-28 04:00:14 +04:00
2011-12-28 01:23:38 +04:00
return $f;
}
}
2011-12-28 04:00:14 +04:00
2011-12-28 01:23:38 +04:00
class sym {
2011-12-28 04:00:14 +04:00
public function __construct($name, $id) {
$this->name = $name;
$this->id = $id;
$this->term = true; // Until proven otherwise.
2011-12-28 01:23:38 +04:00
$this->rule = array();
$this->config = array();
$this->lambda = false;
$this->first = new set();
$this->left_prec = $this->right_prec = 0;
}
2011-12-28 04:00:14 +04:00
public function summary() {
2011-12-28 01:23:38 +04:00
$out = '';
2011-12-28 04:00:14 +04:00
foreach ($this->rule as $rule) {
$out .= $rule->text() . PHP_EOL;
}
2011-12-28 01:23:38 +04:00
return $out;
}
}
2011-12-28 04:00:14 +04:00
2011-12-28 01:23:38 +04:00
class rule {
2011-12-28 04:00:14 +04:00
public function __construct($id, $sym, $rhs, $code, $look, $replace) {
bug_unless(is_int($look));
2011-12-28 01:23:38 +04:00
$this->id = $id;
$this->sym = $sym;
$this->rhs = $rhs;
$this->code = $code;
$this->look = $look;
$this->replace = $replace;
2011-12-28 04:00:14 +04:00
//$this->prec_sym = $prec_sym;
2011-12-28 01:23:38 +04:00
$this->prec = 0;
$this->first = array();
$this->epsilon = count($rhs);
}
2011-12-28 04:00:14 +04:00
public function lhs_glyph() {
return $this->sym->name;
}
public function determine_precedence() {
// We may eventually expand to allow explicit prec_symbol declarations.
// Until then, we'll go with the rightmost terminal, which is what
// BISON does. People probably expect that. The leftmost terminal
// is a reasonable alternative behaviour, but I don't see the big
// deal just now.
//$prec_sym = $this->prec_sym;
//if (!$prec_sym)
2011-12-28 01:23:38 +04:00
$prec_sym = $this->rightmost_terminal();
2011-12-28 04:00:14 +04:00
if (!$prec_sym) {
return;
}
2011-12-28 01:23:38 +04:00
$this->prec = $prec_sym->left_prec;
}
2011-12-28 04:00:14 +04:00
2011-12-28 01:23:38 +04:00
private function rightmost_terminal() {
2011-12-28 04:00:14 +04:00
$symbol = null;
2011-12-28 01:23:38 +04:00
$rhs = $this->rhs;
2011-12-28 04:00:14 +04:00
2011-12-28 01:23:38 +04:00
while ($rhs) {
$symbol = array_pop($rhs);
2011-12-28 04:00:14 +04:00
if ($symbol->term) {
break;
}
2011-12-28 01:23:38 +04:00
}
2011-12-28 04:00:14 +04:00
2011-12-28 01:23:38 +04:00
return $symbol;
}
2011-12-28 04:00:14 +04:00
public function text() {
$t = '(' . $this->id . ') ' . $this->lhs_glyph() . ' :=';
foreach($this->rhs as $s) {
$t .= ' ' . $s->name;
}
2011-12-28 01:23:38 +04:00
return $t;
}
2011-12-28 04:00:14 +04:00
public function table(lime_language $lang) {
2011-12-28 01:23:38 +04:00
return array(
'symbol' => $this->lhs_glyph(),
'len' => $this->look,
'replace' => $this->replace,
'code' => $lang->fixup($this->code),
'text' => $this->text(),
);
}
2011-12-28 04:00:14 +04:00
public function lambda() {
foreach ($this->rhs as $sym) {
if (!$sym->lambda) {
return false;
}
}
2011-12-28 01:23:38 +04:00
return true;
}
2011-12-28 04:00:14 +04:00
public function find_first() {
2011-12-28 01:23:38 +04:00
$dot = count($this->rhs);
2011-12-28 04:00:14 +04:00
$last = $this->first[$dot] = new set();
while ($dot--) {
2011-12-28 01:23:38 +04:00
$symbol_after_the_dot = $this->rhs[$dot];
$first = $symbol_after_the_dot->first->all();
2011-12-28 04:00:14 +04:00
2011-12-28 01:23:38 +04:00
bug_if(empty($first) and !$symbol_after_the_dot->lambda);
2011-12-28 04:00:14 +04:00
2011-12-28 01:23:38 +04:00
$set = new set($first);
if ($symbol_after_the_dot->lambda) {
$set->union($last);
2011-12-28 04:00:14 +04:00
if ($this->epsilon == $dot + 1) {
$this->epsilon = $dot;
}
2011-12-28 01:23:38 +04:00
}
2011-12-28 04:00:14 +04:00
2011-12-28 01:23:38 +04:00
$last = $this->first[$dot] = $set;
}
}
2011-12-28 04:00:14 +04:00
public function teach_symbol_of_first_set() {
2011-12-28 01:23:38 +04:00
$go = false;
foreach ($this->rhs as $sym) {
2011-12-28 04:00:14 +04:00
if ($this->sym->first->union($sym->first)) {
$go = true;
}
if (!$sym->lambda) {
break;
}
2011-12-28 01:23:38 +04:00
}
2011-12-28 04:00:14 +04:00
2011-12-28 01:23:38 +04:00
return $go;
}
2011-12-28 04:00:14 +04:00
public function lambda_from($dot) {
2011-12-28 01:23:38 +04:00
return $this->epsilon <= $dot;
}
2011-12-28 04:00:14 +04:00
public function leftmost($follow) {
2011-12-28 01:23:38 +04:00
return new config($this, 0, $follow);
}
2011-12-28 04:00:14 +04:00
public function dotted_text($dot) {
$out = $this->lhs_glyph() . ' :=';
2011-12-28 01:23:38 +04:00
$idx = -1;
foreach($this->rhs as $idx => $s) {
2011-12-28 04:00:14 +04:00
if ($idx == $dot) {
$out .= ' .';
}
$out .= ' ' . $s->name;
2011-12-28 01:23:38 +04:00
}
2011-12-28 04:00:14 +04:00
if ($dot > $idx) {
$out .= ' .';
}
2011-12-28 01:23:38 +04:00
return $out;
}
}
2011-12-28 04:00:14 +04:00
2011-12-28 01:23:38 +04:00
class config {
2011-12-28 04:00:14 +04:00
public function __construct($rule, $dot, $follow) {
$this->rule = $rule;
2011-12-28 01:23:38 +04:00
$this->dot = $dot;
2011-12-28 04:00:14 +04:00
$this->key = $rule->id . '.' . $dot;
2011-12-28 01:23:38 +04:00
$this->rightmost = count($rule->rhs) <= $dot;
$this->symbol_after_the_dot = $this->rightmost ? null : $rule->rhs[$dot];
$this->_blink = array();
$this->follow = new set($follow);
2011-12-28 04:00:14 +04:00
$this->_flink = array();
2011-12-28 01:23:38 +04:00
bug_unless($this->rightmost or count($rule));
}
2011-12-28 04:00:14 +04:00
public function text() {
return $this->rule->dotted_text($this->dot)
. ' [ ' . implode(' ', $this->follow->all()) . ' ]';
2011-12-28 01:23:38 +04:00
}
2011-12-28 04:00:14 +04:00
public function blink($config) {
2011-12-28 01:23:38 +04:00
$this->_blink[] = $config;
}
2011-12-28 04:00:14 +04:00
public function next() {
2011-12-28 01:23:38 +04:00
bug_if($this->rightmost);
2011-12-28 04:00:14 +04:00
2011-12-28 01:23:38 +04:00
$c = new config($this->rule, $this->dot+1, array());
2011-12-28 04:00:14 +04:00
// Anything in the follow set for this config will also be in the next.
// However, we link it backwards because we might wind up selecting a
// pre-existing state, and the housekeeping is easier in the first half
// of the program. We'll fix it before doing the propagation.
2011-12-28 01:23:38 +04:00
$c->blink($this);
2011-12-28 04:00:14 +04:00
2011-12-28 01:23:38 +04:00
return $c;
}
2011-12-28 04:00:14 +04:00
public function copy_links_from($that) {
foreach($that->_blink as $c) {
$this->blink($c);
}
2011-12-28 01:23:38 +04:00
}
2011-12-28 04:00:14 +04:00
public function lambda() {
2011-12-28 01:23:38 +04:00
return $this->rule->lambda_from($this->dot);
}
2011-12-28 04:00:14 +04:00
public function simple_follow() {
return $this->rule->first[$this->dot + 1]->all();
2011-12-28 01:23:38 +04:00
}
2011-12-28 04:00:14 +04:00
public function epsilon_follows() {
return $this->rule->lambda_from($this->dot + 1);
2011-12-28 01:23:38 +04:00
}
2011-12-28 04:00:14 +04:00
public function fixlinks() {
foreach ($this->_blink as $that) {
$that->_flink[] = $this;
}
2011-12-28 01:23:38 +04:00
$this->blink = array();
}
2011-12-28 04:00:14 +04:00
public function dump() {
echo ' * ';
echo $this->key . ' : ';
2011-12-28 01:23:38 +04:00
echo $this->rule->dotted_text($this->dot);
echo $this->follow->text();
2011-12-28 04:00:14 +04:00
foreach ($this->_flink as $c) {
echo $c->key . ' / ';
}
echo PHP_EOL;
2011-12-28 01:23:38 +04:00
}
}
2011-12-28 04:00:14 +04:00
2011-12-28 01:23:38 +04:00
class lime {
2011-12-28 04:00:14 +04:00
public $parser_class = 'parser';
public function __construct() {
2011-12-28 01:23:38 +04:00
$this->p_next = 1;
$this->sym = array();
$this->rule = array();
$this->start_symbol_set = array();
$this->state = array();
$this->stop = $this->sym('#');
2011-12-28 04:00:14 +04:00
2011-12-28 01:23:38 +04:00
if ($err = $this->sym('error')) {
$err->term = false;
}
2011-12-28 04:00:14 +04:00
2011-12-28 01:23:38 +04:00
$this->lang = new lime_language_php();
}
2011-12-28 04:00:14 +04:00
function language() {
return $this->lang;
}
2011-12-28 01:23:38 +04:00
function build_parser() {
$this->add_start_rule();
2011-12-28 04:00:14 +04:00
foreach ($this->rule as $r) {
$r->determine_precedence();
}
2011-12-28 01:23:38 +04:00
$this->find_sym_lamdba();
$this->find_sym_first();
2011-12-28 04:00:14 +04:00
foreach ($this->rule as $rule) {
$rule->find_first();
}
2011-12-28 01:23:38 +04:00
$initial = $this->find_states();
$this->fixlinks();
2011-12-28 04:00:14 +04:00
// $this->dump_configurations();
2011-12-28 01:23:38 +04:00
$this->find_follow_sets();
2011-12-28 04:00:14 +04:00
foreach($this->state as $s) {
$s->find_reductions($this);
}
2011-12-28 01:23:38 +04:00
$i = $this->resolve_conflicts();
$a = $this->rule_table();
$qi = $initial->id;
2011-12-28 04:00:14 +04:00
2011-12-28 01:23:38 +04:00
return $this->lang->ptab_to_class($this->parser_class, compact('a', 'qi', 'i'));
}
2011-12-28 04:00:14 +04:00
2011-12-28 01:23:38 +04:00
function rule_table() {
$s = array();
2011-12-28 04:00:14 +04:00
2011-12-28 01:23:38 +04:00
foreach ($this->rule as $i => $r) {
$s[$i] = $r->table($this->lang);
}
2011-12-28 04:00:14 +04:00
2011-12-28 01:23:38 +04:00
return $s;
}
2011-12-28 04:00:14 +04:00
2011-12-28 01:23:38 +04:00
function add_rule($symbol, $rhs, $code) {
$this->add_raw_rule($symbol, $rhs, $code, count($rhs), true);
}
2011-12-28 04:00:14 +04:00
2011-12-28 01:23:38 +04:00
function trump_up_bogus_lhs($real) {
2011-12-28 04:00:14 +04:00
return "'{$real}'" . count($this->rule);
2011-12-28 01:23:38 +04:00
}
2011-12-28 04:00:14 +04:00
function add_raw_rule($lhs, $rhs, $code, $look, $replace) {
2011-12-28 01:23:38 +04:00
$sym = $this->sym($lhs);
2011-12-28 04:00:14 +04:00
$sym->term = false;
if (!$rhs) {
$sym->lambda = true;
}
2011-12-28 01:23:38 +04:00
$rs = array();
2011-12-28 04:00:14 +04:00
foreach ($rhs as $str) {
$rs[] = $this->sym($str);
}
2011-12-28 01:23:38 +04:00
$rid = count($this->rule);
$r = new rule($rid, $sym, $rs, $code, $look, $replace);
$this->rule[$rid] = $r;
$sym->rule[] = $r;
}
2011-12-28 04:00:14 +04:00
2011-12-28 01:23:38 +04:00
function sym($str) {
2011-12-28 04:00:14 +04:00
if (!isset($this->sym[$str])) {
$this->sym[$str] = new sym($str, count($this->sym));
}
2011-12-28 01:23:38 +04:00
return $this->sym[$str];
}
2011-12-28 04:00:14 +04:00
2011-12-28 01:23:38 +04:00
function summary() {
$out = '';
2011-12-28 04:00:14 +04:00
foreach ($this->sym as $sym) {
if (!$sym->term) {
$out .= $sym->summary();
}
}
2011-12-28 01:23:38 +04:00
return $out;
}
2011-12-28 04:00:14 +04:00
2011-12-28 01:23:38 +04:00
private function find_sym_lamdba() {
do {
$go = false;
2011-12-28 04:00:14 +04:00
foreach ($this->sym as $sym) {
if (!$sym->lambda) {
foreach ($sym->rule as $rule) {
if ($rule->lambda()) {
$go = true;
$sym->lambda = true;
}
}
2011-12-28 01:23:38 +04:00
}
}
} while ($go);
}
2011-12-28 04:00:14 +04:00
2011-12-28 01:23:38 +04:00
private function teach_terminals_first_set() {
2011-12-28 04:00:14 +04:00
foreach ($this->sym as $sym) {
if ($sym->term) {
$sym->first->add($sym->name);
}
}
2011-12-28 01:23:38 +04:00
}
2011-12-28 04:00:14 +04:00
2011-12-28 01:23:38 +04:00
private function find_sym_first() {
$this->teach_terminals_first_set();
2011-12-28 04:00:14 +04:00
2011-12-28 01:23:38 +04:00
do {
$go = false;
2011-12-28 04:00:14 +04:00
foreach ($this->rule as $r) {
if ($r->teach_symbol_of_first_set()) {
$go = true;
}
}
2011-12-28 01:23:38 +04:00
} while ($go);
}
2011-12-28 04:00:14 +04:00
2011-12-28 01:23:38 +04:00
function add_start_rule() {
$rewrite = new lime_rewrite("'start'");
$rhs = new lime_rhs();
2011-12-28 04:00:14 +04:00
$rhs->add(new lime_glyph($this->deduce_start_symbol()->name, null));
//$rhs->add(new lime_glyph($this->stop->name, null));
2011-12-28 01:23:38 +04:00
$rewrite->add_rhs($rhs);
$rewrite->update($this);
}
2011-12-28 04:00:14 +04:00
2011-12-28 01:23:38 +04:00
private function deduce_start_symbol() {
$candidate = current($this->start_symbol_set);
2011-12-28 04:00:14 +04:00
// Did the person try to set a start symbol at all?
if (!$candidate) {
return $this->first_rule_lhs();
}
// Do we actually have such a symbol on the left of a rule?
if ($candidate->terminal) {
return $this->first_rule_lhs();
}
// Ok, it's a decent choice. We need to return the symbol entry.
2011-12-28 01:23:38 +04:00
return $this->sym($candidate);
}
2011-12-28 04:00:14 +04:00
2011-12-28 01:23:38 +04:00
private function first_rule_lhs() {
reset($this->rule);
$r = current($this->rule);
return $r->sym;
}
2011-12-28 04:00:14 +04:00
/**
* Build an initial state. This is a recursive process which digs out
* the LR(0) state graph.
*/
2011-12-28 01:23:38 +04:00
function find_states() {
$start_glyph = "'start'";
$sym = $this->sym($start_glyph);
$basis = array();
2011-12-28 04:00:14 +04:00
2011-12-28 01:23:38 +04:00
foreach($sym->rule as $rule) {
$c = $rule->leftmost(array('#'));
$basis[$c->key] = $c;
}
2011-12-28 04:00:14 +04:00
2011-12-28 01:23:38 +04:00
$initial = $this->get_state($basis);
$initial->add_accept($sym);
2011-12-28 04:00:14 +04:00
2011-12-28 01:23:38 +04:00
return $initial;
}
2011-12-28 04:00:14 +04:00
2011-12-28 01:23:38 +04:00
function get_state($basis) {
$key = array_keys($basis);
sort($key);
$key = implode(' ', $key);
2011-12-28 04:00:14 +04:00
2011-12-28 01:23:38 +04:00
if (isset($this->state[$key])) {
2011-12-28 04:00:14 +04:00
// Copy all the links around...
2011-12-28 01:23:38 +04:00
$state = $this->state[$key];
2011-12-28 04:00:14 +04:00
foreach($basis as $config) {
$state->close[$config->key]->copy_links_from($config);
}
2011-12-28 01:23:38 +04:00
return $state;
} else {
$close = $this->state_closure($basis);
$this->state[$key] = $state = new state(count($this->state), $key, $close);
$this->build_shifts($state);
2011-12-28 04:00:14 +04:00
2011-12-28 01:23:38 +04:00
return $state;
}
}
2011-12-28 04:00:14 +04:00
2011-12-28 01:23:38 +04:00
private function state_closure($q) {
2011-12-28 04:00:14 +04:00
// $q is a list of config.
2011-12-28 01:23:38 +04:00
$close = array();
while ($config = array_pop($q)) {
if (isset($close[$config->key])) {
$close[$config->key]->copy_links_from($config);
$close[$config->key]->follow->union($config->follow);
continue;
}
2011-12-28 04:00:14 +04:00
2011-12-28 01:23:38 +04:00
$close[$config->key] = $config;
2011-12-28 04:00:14 +04:00
2011-12-28 01:23:38 +04:00
$symbol_after_the_dot = $config->symbol_after_the_dot;
2011-12-28 04:00:14 +04:00
if (!$symbol_after_the_dot) {
continue;
}
if (!$symbol_after_the_dot->term) {
2011-12-28 01:23:38 +04:00
foreach ($symbol_after_the_dot->rule as $r) {
$station = $r->leftmost($config->simple_follow());
2011-12-28 04:00:14 +04:00
if ($config->epsilon_follows()) {
$station->blink($config);
}
2011-12-28 01:23:38 +04:00
$q[] = $station;
}
2011-12-28 04:00:14 +04:00
// The following turned out to be wrong. Don't do it.
//if ($symbol_after_the_dot->lambda) {
// $q[] = $config->next();
//}
2011-12-28 01:23:38 +04:00
}
}
2011-12-28 04:00:14 +04:00
2011-12-28 01:23:38 +04:00
return $close;
}
2011-12-28 04:00:14 +04:00
2011-12-28 01:23:38 +04:00
function build_shifts($state) {
foreach ($state->segment_config() as $glyph => $segment) {
$basis = array();
foreach ($segment as $preshift) {
$postshift = $preshift->next();
$basis[$postshift->key] = $postshift;
}
2011-12-28 04:00:14 +04:00
2011-12-28 01:23:38 +04:00
$dest = $this->get_state($basis);
$state->add_shift($this->sym($glyph), $dest);
}
}
2011-12-28 04:00:14 +04:00
2011-12-28 01:23:38 +04:00
function fixlinks() {
2011-12-28 04:00:14 +04:00
foreach ($this->state as $s) {
foreach ($s->close as $c) {
$c->fixlinks();
}
}
2011-12-28 01:23:38 +04:00
}
2011-12-28 04:00:14 +04:00
2011-12-28 01:23:38 +04:00
function find_follow_sets() {
$q = array();
2011-12-28 04:00:14 +04:00
foreach ($this->state as $s) {
foreach ($s->close as $c) {
$q[] = $c;
}
}
2011-12-28 01:23:38 +04:00
while ($q) {
$c = array_shift($q);
2011-12-28 04:00:14 +04:00
2011-12-28 01:23:38 +04:00
foreach ($c->_flink as $d) {
2011-12-28 04:00:14 +04:00
if ($d->follow->union($c->follow)) {
$q[] = $d;
}
2011-12-28 01:23:38 +04:00
}
}
}
2011-12-28 04:00:14 +04:00
2011-12-28 01:23:38 +04:00
private function set_assoc($ss, $l, $r) {
2011-12-28 04:00:14 +04:00
$p = ($this->p_next++) * 2;
2011-12-28 01:23:38 +04:00
foreach ($ss as $glyph) {
$s = $this->sym($glyph);
2011-12-28 04:00:14 +04:00
$s->left_prec = $p + $l;
$s->right_prec = $p + $r;
2011-12-28 01:23:38 +04:00
}
}
2011-12-28 04:00:14 +04:00
function left_assoc($ss) {
$this->set_assoc($ss, 1, 0);
}
function right_assoc($ss) {
$this->set_assoc($ss, 0, 1);
}
function non_assoc($ss) {
$this->set_assoc($ss, 0, 0);
}
2011-12-28 01:23:38 +04:00
private function resolve_conflicts() {
2011-12-28 04:00:14 +04:00
// For each state, try to find one and only one
// thing to do for any given lookahead.
2011-12-28 01:23:38 +04:00
$i = array();
2011-12-28 04:00:14 +04:00
foreach ($this->state as $s) {
$i[$s->id] = $s->resolve_conflicts();
}
2011-12-28 01:23:38 +04:00
return $i;
}
2011-12-28 04:00:14 +04:00
2011-12-28 01:23:38 +04:00
function dump_configurations() {
2011-12-28 04:00:14 +04:00
foreach ($this->state as $q) {
$q->dump();
}
2011-12-28 01:23:38 +04:00
}
2011-12-28 04:00:14 +04:00
2011-12-28 01:23:38 +04:00
function dump_first_sets() {
foreach ($this->sym as $s) {
2011-12-28 04:00:14 +04:00
echo ' * ';
echo $s->name . ' : ';
2011-12-28 01:23:38 +04:00
echo $s->first->text();
2011-12-28 04:00:14 +04:00
echo PHP_EOL;
2011-12-28 01:23:38 +04:00
}
}
2011-12-28 04:00:14 +04:00
2011-12-28 01:23:38 +04:00
function add_rule_with_actions($lhs, $rhs) {
2011-12-28 04:00:14 +04:00
// First, make sure this thing is well-formed.
if(!is_object(end($rhs))) {
$rhs[] = new cf_action('');
}
// Now, split it into chunks based on the actions.
2011-12-28 01:23:38 +04:00
$look = -1;
$subrule = array();
$subsymbol = '';
2011-12-28 04:00:14 +04:00
while ($rhs) {
2011-12-28 01:23:38 +04:00
$it = array_shift($rhs);
2011-12-28 04:00:14 +04:00
++$look;
2011-12-28 01:23:38 +04:00
if (is_string($it)) {
$subrule[] = $it;
} else {
$code = $it->code;
2011-12-28 04:00:14 +04:00
// It's an action.
// Is it the last one?
if ($rhs) {
// no.
2011-12-28 01:23:38 +04:00
$subsymbol = $this->trump_up_bogus_lhs($lhs);
$this->add_raw_rule($subsymbol, $subrule, $code, $look, false);
$subrule = array($subsymbol);
} else {
2011-12-28 04:00:14 +04:00
// yes.
2011-12-28 01:23:38 +04:00
$this->add_raw_rule($lhs, $subrule, $code, $look, true);
}
}
}
}
2011-12-28 04:00:14 +04:00
2011-12-28 01:23:38 +04:00
function pragma($type, $args) {
switch ($type) {
2011-12-28 04:00:14 +04:00
case 'left':
2011-12-28 01:23:38 +04:00
$this->left_assoc($args);
break;
2011-12-28 04:00:14 +04:00
case 'right':
2011-12-28 01:23:38 +04:00
$this->right_assoc($args);
break;
2011-12-28 04:00:14 +04:00
case 'nonassoc':
2011-12-28 01:23:38 +04:00
$this->non_assoc($args);
break;
2011-12-28 04:00:14 +04:00
case 'start':
2011-12-28 01:23:38 +04:00
$this->start_symbol_set = $args;
break;
2011-12-28 04:00:14 +04:00
case 'class':
2011-12-28 01:23:38 +04:00
$this->parser_class = $args[0];
break;
2011-12-28 04:00:14 +04:00
default:
emit(sprintf('Bad Parser Pragma: (%s)', $type));
2011-12-28 01:23:38 +04:00
exit(1);
}
}
}
2011-12-28 04:00:14 +04:00
class lime_language {
}
2011-12-28 01:23:38 +04:00
class lime_language_php extends lime_language {
2011-12-28 04:00:14 +04:00
protected function result_code($expr) {
return '$result = ' . $expr . ';' . PHP_EOL;
}
public function default_result() {
return $this->result_code('reset($tokens)');
}
public function result_pos($pos) {
return $this->result_code(lime_token_reference($pos));
}
public function bind($name, $pos) {
return '$' . $name . ' = &$tokens[' . $pos . '];' . PHP_EOL;
2011-12-28 01:23:38 +04:00
}
2011-12-28 04:00:14 +04:00
public function fixup($code) {
return preg_replace_callback('~\$(\d+|\$)~', function ($foo) {
if ($foo[1] === '$') {
// always
return '$result';
}
return lime_token_reference($foo[1] - 1);
}, $code);
}
2011-12-28 01:23:38 +04:00
function to_php($code) {
return $code;
}
2011-12-28 04:00:14 +04:00
public function ptab_to_class($parser_class, $ptab) {
$code = '';
$code .= 'public $qi = ' . lime_export($ptab['qi'], true) . ';' . PHP_EOL;
$code .= 'public $i = '.lime_export($ptab['i'], true).';' . PHP_EOL;
2011-12-28 01:23:38 +04:00
$rc = array();
$method = array();
$rules = array();
2011-12-28 04:00:14 +04:00
2011-12-28 01:23:38 +04:00
foreach($ptab['a'] as $k => $a) {
$symbol = preg_replace('/[^\w]/', '', $a['symbol']);
2011-12-28 04:00:14 +04:00
$rn = @++$rc[$symbol];
$mn = 'reduce_' . $k . '_' . $symbol . '_' . $rn;
2011-12-28 01:23:38 +04:00
$method[$k] = $mn;
2011-12-28 04:00:14 +04:00
$comment = '// ' . $a['text'] . PHP_EOL;
2011-12-28 01:23:38 +04:00
$php = $this->to_php($a['code']);
2011-12-28 04:00:14 +04:00
$code .= 'function ' . $mn . '(' . LIME_CALL_PROTOCOL . ') {' . PHP_EOL .
preg_replace('~^~m', "\t", $comment . $php) . PHP_EOL .
'}' .
PHP_EOL .
PHP_EOL;
2011-12-28 01:23:38 +04:00
unset($a['code']);
unset($a['text']);
$rules[$k] = $a;
}
2011-12-28 04:00:14 +04:00
$code .= 'public $method = ' . lime_export($method, true) . ';' . PHP_EOL;
$code .= 'public $a = '.lime_export($rules, true) . ';' . PHP_EOL;
return 'class ' . $parser_class . ' extends lime_parser {' . PHP_EOL .
preg_replace(array('~^~m', '~^\h+$~m'), array("\t", ''), $code) .
'}' . PHP_EOL;
2011-12-28 01:23:38 +04:00
}
}
2011-12-28 04:00:14 +04:00
2011-12-28 01:23:38 +04:00
class lime_rhs {
function __construct() {
2011-12-28 04:00:14 +04:00
// Construct and add glyphs and actions in whatever order.
// Then, add this to a lime_rewrite.
//
// Don't call install_rule.
// The rewrite will do that for you when you "update" with it.
2011-12-28 01:23:38 +04:00
$this->rhs = array();
}
2011-12-28 04:00:14 +04:00
function add(lime_slot $slot) {
2011-12-28 01:23:38 +04:00
$this->rhs[] = $slot;
}
2011-12-28 04:00:14 +04:00
2011-12-28 01:23:38 +04:00
function install_rule(lime $lime, $lhs) {
2011-12-28 04:00:14 +04:00
// This is the part that has to break the rule into subrules if necessary.
2011-12-28 01:23:38 +04:00
$rhs = $this->rhs;
2011-12-28 04:00:14 +04:00
// First, make sure this thing is well-formed.
if (!(end($rhs) instanceof lime_action)) {
$rhs[] = new lime_action('', null);
}
// Now, split it into chunks based on the actions.
2011-12-28 01:23:38 +04:00
$lang = $lime->language();
$result_code = $lang->default_result();
$look = -1;
$subrule = array();
$subsymbol = '';
$preamble = '';
2011-12-28 04:00:14 +04:00
while ($rhs) {
2011-12-28 01:23:38 +04:00
$it = array_shift($rhs);
2011-12-28 04:00:14 +04:00
++$look;
2011-12-28 01:23:38 +04:00
if ($it instanceof lime_glyph) {
$subrule[] = $it->data;
} elseif ($it instanceof lime_action) {
2011-12-28 04:00:14 +04:00
$code = unindent($it->data);
// It's an action.
// Is it the last one?
if ($rhs) {
// no.
2011-12-28 01:23:38 +04:00
$subsymbol = $lime->trump_up_bogus_lhs($lhs);
2011-12-28 04:00:14 +04:00
$action = $lang->default_result() . $preamble . $code;
2011-12-28 01:23:38 +04:00
$lime->add_raw_rule($subsymbol, $subrule, $action, $look, false);
$subrule = array($subsymbol);
} else {
2011-12-28 04:00:14 +04:00
// yes.
$action = $result_code . $preamble . $code;
2011-12-28 01:23:38 +04:00
$lime->add_raw_rule($lhs, $subrule, $action, $look, true);
}
} else {
impossible();
}
2011-12-28 04:00:14 +04:00
if ($it->name == '$') {
$result_code = $lang->result_pos($look);
} elseif ($it->name) {
$preamble .= $lang->bind($it->name, $look);
}
2011-12-28 01:23:38 +04:00
}
}
}
2011-12-28 04:00:14 +04:00
2011-12-28 01:23:38 +04:00
class lime_rewrite {
function __construct($glyph) {
2011-12-28 04:00:14 +04:00
// Construct one of these with the name of the lhs.
// Add some rhs-es to it.
// Finally, "update" the lime you're building.
2011-12-28 01:23:38 +04:00
$this->glyph = $glyph;
$this->rhs = array();
}
2011-12-28 04:00:14 +04:00
function add_rhs(lime_rhs $rhs) {
2011-12-28 01:23:38 +04:00
$this->rhs[] = $rhs;
}
2011-12-28 04:00:14 +04:00
2011-12-28 01:23:38 +04:00
function update(lime $lime) {
foreach ($this->rhs as $rhs) {
$rhs->install_rule($lime, $this->glyph);
}
}
}
2011-12-28 04:00:14 +04:00
2011-12-28 01:23:38 +04:00
class lime_slot {
2011-12-28 04:00:14 +04:00
// This keeps track of one position in an rhs.
// We specialize to handle actions and glyphs.
// If there is a name for the slot, we store it here.
// Later on, this structure will be consulted in the formation of
// actual production rules.
public function __construct($data, $name) {
2011-12-28 01:23:38 +04:00
$this->data = $data;
$this->name = $name;
}
2011-12-28 04:00:14 +04:00
public function preamble($pos) {
2011-12-28 01:23:38 +04:00
if (strlen($this->name) > 0) {
2011-12-28 04:00:14 +04:00
return '$' . $this->name . ' = &$tokens[' . $pos . '];' . PHP_EOL;
2011-12-28 01:23:38 +04:00
}
}
}
2011-12-28 04:00:14 +04:00
class lime_glyph extends lime_slot {
}
class lime_action extends lime_slot {
}
2011-12-28 01:23:38 +04:00
function lime_bootstrap() {
2011-12-28 04:00:14 +04:00
2011-12-28 01:23:38 +04:00
/*
2011-12-28 04:00:14 +04:00
2011-12-28 01:23:38 +04:00
This function isn't too terribly interesting to the casual observer.
You're probably better off looking at parse_lime_grammar() instead.
2011-12-28 04:00:14 +04:00
2011-12-28 01:23:38 +04:00
Ok, if you insist, I'll explain.
2011-12-28 04:00:14 +04:00
2011-12-28 01:23:38 +04:00
The input to Lime is a CFG parser definition. That definition is
written in some language. (The Lime language, to be exact.)
Anyway, I have to parse the Lime language and compile it into a
very complex data structure from which a parser is eventually
built. What better way than to use Lime itself to parse its own
language? Well, it's almost that simple, but not quite.
2011-12-28 04:00:14 +04:00
2011-12-28 01:23:38 +04:00
The Lime language is fairly potent, but a restricted subset of
its features was used to write a metagrammar. Then, I hand-translated
that metagrammar into another form which is easy to snarf up.
2011-12-28 04:00:14 +04:00
In the process of reading that simplified form, this function
2011-12-28 01:23:38 +04:00
builds the same sort of data structure that later gets turned into
a parser. The last step is to run the parser generation algorithm,
eval() the resulting PHP code, and voila! With no hard work, I can
suddenly read and comprehend the full range of the Lime language
without ever having written an algorithm to do so. It feels like magic.
2011-12-28 04:00:14 +04:00
2011-12-28 01:23:38 +04:00
*/
2011-12-28 04:00:14 +04:00
$bootstrap = LIME_DIR . '/lime.bootstrap';
2011-12-28 01:23:38 +04:00
$lime = new lime();
$lime->parser_class = 'lime_metaparser';
$rhs = array();
2011-12-28 04:00:14 +04:00
2011-12-28 01:23:38 +04:00
bug_unless(is_readable($bootstrap));
2011-12-28 04:00:14 +04:00
2011-12-28 01:23:38 +04:00
foreach(file($bootstrap) as $l) {
2011-12-28 04:00:14 +04:00
$a = explode(':', $l, 2);
2011-12-28 01:23:38 +04:00
if (count($a) == 2) {
list($pattern, $code) = $a;
$sl = new lime_rhs();
$pattern = trim($pattern);
2011-12-28 04:00:14 +04:00
if (strlen($pattern) > 0) {
foreach (explode(' ', $pattern) as $glyph) {
$sl->add(new lime_glyph($glyph, null));
}
2011-12-28 01:23:38 +04:00
}
2011-12-28 04:00:14 +04:00
2011-12-28 01:23:38 +04:00
$sl->add(new lime_action($code, NULL));
$rhs[] = $sl;
} else {
2011-12-28 04:00:14 +04:00
if (preg_match('~^to (\w+)$~', $l, $r)) {
$g = $r[1];
$rw = new lime_rewrite($g);
foreach($rhs as $b) {
$rw->add_rhs($b);
}
$rw->update($lime);
$rhs = array();
}
2011-12-28 01:23:38 +04:00
}
}
2011-12-28 04:00:14 +04:00
2011-12-28 01:23:38 +04:00
$parser_code = $lime->build_parser();
eval($parser_code);
}
class voodoo_scanner extends flex_scanner {
/*
2011-12-28 04:00:14 +04:00
2011-12-28 01:23:38 +04:00
The voodoo is in the way I do lexical processing on grammar definition
files. They contain embedded bits of PHP, and it's important to keep
track of things like strings, comments, and matched braces. It seemed
like an ideal problem to solve with GNU flex, so I wrote a little
scanner in flex and C to dig out the tokens for me. Of course, I need
the tokens in PHP, so I designed a simple binary wrapper for them which
also contains line-number information, guaranteed to help out if you
write a grammar which surprises the parser in any manner.
2011-12-28 04:00:14 +04:00
2011-12-28 01:23:38 +04:00
*/
function executable() { return LIME_DIR.'/lime_scan_tokens'; }
}
function parse_lime_grammar($path) {
/*
2011-12-28 04:00:14 +04:00
2011-12-28 01:23:38 +04:00
This is a good function to read because it teaches you how to interface
with a Lime parser. I've tried to isolate out the bits that aren't
instructive in that regard.
2011-12-28 04:00:14 +04:00
2011-12-28 01:23:38 +04:00
*/
if (!class_exists('lime_metaparser')) lime_bootstrap();
2011-12-28 04:00:14 +04:00
2011-12-28 01:23:38 +04:00
$parse_engine = new parse_engine(new lime_metaparser());
$scanner = new voodoo_scanner($path);
2011-12-28 04:00:14 +04:00
2011-12-28 01:23:38 +04:00
try {
2011-12-28 04:00:14 +04:00
// The result of parsing a Lime grammar is a Lime object.
2011-12-28 01:23:38 +04:00
$lime = $scanner->feed($parse_engine);
2011-12-28 04:00:14 +04:00
// Calling its build_parser() method gets the output PHP code.
2011-12-28 01:23:38 +04:00
return $lime->build_parser();
} catch (parse_error $e) {
2011-12-28 04:00:14 +04:00
die ($e->getMessage() . " in {$path} line {$scanner->lineno}." . PHP_EOL);
2011-12-28 01:23:38 +04:00
}
}
if ($_SERVER['argv']) {
$code = '';
array_shift($_SERVER['argv']); # Strip out the program name.
foreach ($_SERVER['argv'] as $path) {
$code .= parse_lime_grammar($path);
}
2011-12-28 04:00:14 +04:00
echo <<<CODE
<?php
2011-12-28 01:23:38 +04:00
/*
2011-12-28 04:00:14 +04:00
*** DON'T EDIT THIS FILE! ***
*
* This file was automatically generated by the Lime parser generator.
* The real source code you should be looking at is in one or more
* grammar files in the Lime format.
*
* THE ONLY REASON TO LOOK AT THIS FILE is to see where in the grammar
* file that your error happened, because there are enough comments to
* help you debug your grammar.
2011-12-28 01:23:38 +04:00
2011-12-28 04:00:14 +04:00
* If you ignore this warning, you're shooting yourself in the brain,
* not the foot.
*/
{$code}
CODE;
2011-12-28 01:23:38 +04:00
}