<?php
/* vim: set expandtab tabstop=4 shiftwidth=4 foldmethod=marker: */
require_once('Structures/Grammar.php');

/**
 * This class represents a BNF grammar. It can be used, with Text_Parser_Generator 
 * to generate a parser for BNF grammar descriptions
 *
 * The described grammar can be represented in BNF as:
 *   <syntax>              ::= <rule> | <rule> <syntax>
 *   <rule>                ::= <opt-whitespace> "<" <rule-name> ">" <rule-priority> "::=" 
 *                             <opt-whitespace> <expression> <line-end> <reduction-code>
 *   <rule-priority>       ::= <opt-whitespace> | "(" <unquoted-text> ")" <opt-whitespace>
 *   <opt-whitespace>      ::= " " <opt-whitespace> | ""  
 *   <expression>          ::= <list> | <list> <opt-whitespace> "|" <opt-whitespace> <expression>
 *   <line-end>            ::= <opt-whitespace> <EOL> | <line-end> <line-end>
 *   <list>                ::= <term> | <term> <opt-whitespace> <list>
 *   <term>                ::= <named-term> | <unnamed-term>
 *   <named-term>          ::= <unnamed-term> <opt-whitespace> "(" <unquoted-text> ")"
 *   <unnamed-term>        ::= <literal> | "<" <rule-name> ">" | '""'
 *   <literal>             ::= '"' <double-quoted-text> '"' | "'" <single-quoted-text> "'"
 *   <double-quoted-text>  ::= <quoted-text-part> | "'" | <double-quoted-text> <quoted-text-part> | <double-quoted-text> "'"
 *   <single-quoted-text>  ::= <quoted-text-part> | '"' | <single-quoted-text> <quoted-text-part> | <single-quoted-text> '"'
 *   <quoted-text-part>    ::= "(" | ")" | "<" | ">" | "::=" | " " | '""' | "|" | <text-terminal> | <quoted-character>
 *   <rule-name>           ::= <unquoted-text>
 *   <unquoted-text>       ::= <text-terminal> | <quoted-character> | <unquoted-text><text-terminal> | <unquoted-text><quoted-character>
 *   <reduction-code>      ::= "<<<" <line-end> <reduction-code-text> <EOL> ">>>" <line-end> | ""
 *   <reduction-code-text> ::= <reduction-code-text-part>
 *   <reduction-code-text> ::= <reduction-code-text-part> <reduction-code-text>
 *   <reduction-code-text-part> ::= <EOL> | "<<<" | ">>>" | '"' | "'" | "(" | ")" | "<" | ">" | "::=" | " " | '""' | "|" | <text-terminal> | <quoted-character>
 */
class Text_Parser_BNF_Grammar extends Structures_Grammar
{
    public function 
__construct()
    {
        
parent::__construct(truefalse);
        require_once(
'Structures/Grammar/Rule.php');
        require_once(
'Structures/Grammar/Symbol.php');
        
$identityFunction =<<<EOS
\$result = func_get_arg(0)->getValue();
EOS;
        
$concatenateFunction =<<<EOS
\$result = '';
foreach (func_get_args() as \$arg) \$result .= \$arg->getValue();
EOS;

        
/*                       |      Production      |  Rule                            |*/
        
$this->addContextFreeRule('S',                  '<grammar>');
        
$r $this->addContextFreeRule('<grammar>',                  '<syntax>');
        
$r->addReductionFunctionSymbolmap(0'$syntax');
        
$r->setReductionFunction(<<<EOS
\$result = \$syntax->getValue();
\$result->computeTerminals();
\$result->setContextFree(true);
EOS
        );
        
$r $this->addContextFreeRule('<syntax>',           '<rule>');
        
$r->addReductionFunctionSymbolmap(0'$rule');
        
$r->setReductionFunction(<<<EOS
require_once('Structures/Grammar.php');
\$result = new Structures_Grammar();
\$result->setContextFree(false);
\$result->setRegular(false);
foreach(\$rule->getValue() as \$r) \$result->addRule(\$r);
EOS
        );

        
$r $this->addContextFreeRule('<syntax>',           '<syntax>','<rule>');
        
$r->addReductionFunctionSymbolmap(0'$syntax');
        
$r->addReductionFunctionSymbolmap(1'$rule');
        
$r->setReductionFunction(<<<EOS
\$result = \$syntax->getValue();
foreach(\$rule->getValue() as \$r) \$result->addRule(\$r);
\$result->computeTerminals();
EOS
        );

        
$r $this->addContextFreeRule('<rule>',             '<opt-whitespace>','<','<rule-name>','>','<rule-priority>','::=','<opt-whitespace>','<expression>''<line-end>''<reduction-code>');
        
$r->addReductionFunctionSymbolmap(2'$ruleName');
        
$r->addReductionFunctionSymbolmap(4'$priority');
        
$r->addReductionFunctionSymbolmap(7'$expression');
        
$r->addReductionFunctionSymbolmap(9'$code');
        
$r->setReductionFunction(<<<EOS
require_once('Structures/Grammar/Symbol.php');
\$result =& \$expression->getValue();
\$ruleName = '<' . \$ruleName->getValue() . '>';
foreach(\$result as \$i => \$rule) {
    \$result[\$i]->addSymbolToLeft(Structures_Grammar_Symbol::create(\$ruleName));
    \$result[\$i]->setReductionFunction(\$code->getValue());
    \$result[\$i]->setPriority(\$priority->getValue());
}
EOS
        );
        
$r $this->addContextFreeRule('<rule-priority>',    '<opt-whitespace>');
        
$r->setReductionFunction('$result = 0;');
        
$r $this->addContextFreeRule('<rule-priority>',    '(','<unquoted-text>',')','<opt-whitespace>');
        
$r->addReductionFunctionSymbolmap(1'$text');
        
$r->setReductionFunction(<<<EOS
\$result = (int) \$text->getValue();
EOS
        );
        
$this->addContextFreeRule('<line-end>',         '<opt-whitespace>','<EOL>');
        
$this->addContextFreeRule('<line-end>',         '<line-end>','<EOL>');
        
$this->addContextFreeRule('<opt-whitespace>',   ' ''<opt-whitespace>');
        
$this->addContextFreeRule('<opt-whitespace>'    )->setPriority(-1);
        
$r $this->addContextFreeRule('<expression>',       '<list>');
        
$r->addReductionFunctionSymbolmap(0'$list');
        
$r->setReductionFunction(<<<EOS
require_once('Structures/Grammar/Rule.php');
require_once('Structures/Grammar/Symbol.php');

\$newRule = new Structures_Grammar_Rule();
foreach(\$list->getValue() as \$idx => \$term) if (!is_null(\$term)) {
    \$newRule->addSymbolToRight(Structures_Grammar_Symbol::create(\$term['term']));
    if (\$term['reductionArgumentName'] != '') \$newRule->addReductionFunctionSymbolmap(\$newRule->rightCount()-1, '$' . \$term['reductionArgumentName']);
}
\$result = array(\$newRule);
EOS
        );
        
$r $this->addContextFreeRule('<expression>',       '<expression>','<opt-whitespace>','|','<opt-whitespace>','<list>');
        
$r->addReductionFunctionSymbolmap(0'$expression');
        
$r->addReductionFunctionSymbolmap(4'$list');
        
$r->setReductionFunction(<<<EOS
require_once('Structures/Grammar/Rule.php');
require_once('Structures/Grammar/Symbol.php');

\$newRule = new Structures_Grammar_Rule();
foreach(\$list->getValue() as \$idx => \$term) if (!is_null(\$term)) {
    \$newRule->addSymbolToRight(Structures_Grammar_Symbol::create(\$term['term']));
    if (\$term['reductionArgumentName'] != '') \$newRule->addReductionFunctionSymbolmap(\$newRule->rightCount()-1, '$' . \$term['reductionArgumentName']);
}
\$result = \$expression->getValue();
\$result[] =& \$newRule;
EOS
        );
        
$r $this->addContextFreeRule('<list>',             '<term>');
        
$r->addReductionFunctionSymbolmap(0'$term');
        
$r->setReductionFunction(<<<EOS
\$result = array(\$term->getValue());
EOS
        );
        
$r $this->addContextFreeRule('<list>',             '<list>','<opt-whitespace>','<term>');
        
$r->addReductionFunctionSymbolmap(0'$list');
        
$r->addReductionFunctionSymbolmap(2'$term');
        
$r->setReductionFunction(<<<EOS
\$result =& \$list->getValue();
\$result[] =& \$term->getValue();
EOS
        );
        
$r $this->addContextFreeRule('<term>',             '<named-term>');
        
$r->addReductionFunctionSymbolmap(0'$term');
        
$r->setReductionFunction($identityFunction);
        
$r $this->addContextFreeRule('<term>',             '<unnamed-term>');
        
$r->addReductionFunctionSymbolmap(0'$term');
        
$r->setReductionFunction($identityFunction);
        
$r $this->addContextFreeRule('<named-term>',       '<unnamed-term>''(''<unquoted-text>'')');
        
$r->addReductionFunctionSymbolmap(0'$unnamedTerm');
        
$r->addReductionFunctionSymbolmap(2'$reductionArgumentName');
        
$r->setReductionFunction(<<<EOS
\$unnamedTerm = \$unnamedTerm->getValue();
\$unnamedTerm = \$unnamedTerm['term'];
\$result = array(
    'reductionArgumentName' => \$reductionArgumentName->getValue(),
    'term' => \$unnamedTerm);
EOS
        );
        
$r $this->addContextFreeRule('<unnamed-term>',     '<literal>');
        
$r->addReductionFunctionSymbolmap(0'$literal');
        
$r->setReductionFunction(<<<EOS
\$result = array(
    'reductionArgumentName' => '',
    'term' => \$literal->getValue());
EOS
        );
        
$r $this->addContextFreeRule('<unnamed-term>',     '<','<rule-name>','>');
        
$r->addReductionFunctionSymbolmap(1'$ruleName');
        
$r->setReductionFunction(<<<EOS
\$result = array(
    'reductionArgumentName' => '',
    'term' => '<' . \$ruleName->getValue() . '>');
EOS
        );
        
$r $this->addContextFreeRule('<unnamed-term>',     '""');
        
$r->addReductionFunctionSymbolmap(0'$literal');
        
$r->setReductionFunction(<<<EOS
\$result = null;
EOS
        );

        
$r $this->addContextFreeRule('<literal>',          '"','<double-quoted-text>','"');
        
$r->addReductionFunctionSymbolmap(1'$textb');
        
$r->setReductionFunction($concatenateFunction);
        
$r $this->addContextFreeRule('<literal>',          '\'','<single-quoted-text>','\'');
        
$r->addReductionFunctionSymbolmap(1'$textb');
        
$r->setReductionFunction($concatenateFunction);

        
$r $this->addContextFreeRule('<double-quoted-text>','<quoted-text-part>');
        
$r->addReductionFunctionSymbolmap(0'$text');
        
$r->setReductionFunction($identityFunction);
        
$r $this->addContextFreeRule('<double-quoted-text>','\'');
        
$r->addReductionFunctionSymbolmap(0'$text');
        
$r->setReductionFunction($identityFunction);
        
$r $this->addContextFreeRule('<double-quoted-text>','<double-quoted-text>','<quoted-text-part>');
        
$r->addReductionFunctionSymbolmap(0'$texta');
        
$r->addReductionFunctionSymbolmap(1'$textb');
        
$r->setReductionFunction($concatenateFunction);
        
$r $this->addContextFreeRule('<double-quoted-text>','<double-quoted-text>','\'');
        
$r->addReductionFunctionSymbolmap(0'$texta');
        
$r->addReductionFunctionSymbolmap(1'$textb');
        
$r->setReductionFunction($concatenateFunction);
        
$r $this->addContextFreeRule('<single-quoted-text>','<quoted-text-part>');
        
$r->addReductionFunctionSymbolmap(0'$text');
        
$r->setReductionFunction($identityFunction);
        
$r $this->addContextFreeRule('<single-quoted-text>','"');
        
$r->addReductionFunctionSymbolmap(0'$text');
        
$r->setReductionFunction($identityFunction);
        
$r $this->addContextFreeRule('<single-quoted-text>','<single-quoted-text>','<quoted-text-part>');
        
$r->addReductionFunctionSymbolmap(0'$texta');
        
$r->addReductionFunctionSymbolmap(1'$textb');
        
$r->setReductionFunction($concatenateFunction);
        
$r $this->addContextFreeRule('<single-quoted-text>','<single-quoted-text>','"');
        
$r->addReductionFunctionSymbolmap(0'$texta');
        
$r->addReductionFunctionSymbolmap(1'$textb');
        
$r->setReductionFunction($concatenateFunction);
        
$r $this->addContextFreeRule('<quoted-text-part>','<text-terminal>');
        
$r->addReductionFunctionSymbolmap(0'$text');
        
$r->setReductionFunction($identityFunction);
        
$r $this->addContextFreeRule('<quoted-text-part>','<quoted-character>');
        
$r->addReductionFunctionSymbolmap(0'$text');
        
$r->setReductionFunction(<<<EOS
\$result = \$text->getValue();
\$result = \$result[1];
EOS
        );
        
$r $this->addContextFreeRule('<quoted-text-part>','>>>');
        
$r->addReductionFunctionSymbolmap(0'$text');
        
$r->setReductionFunction($identityFunction);
        
$r $this->addContextFreeRule('<quoted-text-part>','<<<');
        
$r->addReductionFunctionSymbolmap(0'$text');
        
$r->setReductionFunction($identityFunction);
        
$r $this->addContextFreeRule('<quoted-text-part>','(');
        
$r->addReductionFunctionSymbolmap(0'$text');
        
$r->setReductionFunction($identityFunction);
        
$r $this->addContextFreeRule('<quoted-text-part>',')');
        
$r->addReductionFunctionSymbolmap(0'$text');
        
$r->setReductionFunction($identityFunction);
        
$r $this->addContextFreeRule('<quoted-text-part>',' ');
        
$r->addReductionFunctionSymbolmap(0'$text');
        
$r->setReductionFunction($identityFunction);
        
$r $this->addContextFreeRule('<quoted-text-part>','<');
        
$r->addReductionFunctionSymbolmap(0'$text');
        
$r->setReductionFunction($identityFunction);
        
$r $this->addContextFreeRule('<quoted-text-part>','>');
        
$r->addReductionFunctionSymbolmap(0'$text');
        
$r->setReductionFunction($identityFunction);
        
$r $this->addContextFreeRule('<quoted-text-part>','|');
        
$r->addReductionFunctionSymbolmap(0'$text');
        
$r->setReductionFunction($identityFunction);
        
$r $this->addContextFreeRule('<quoted-text-part>','::=');
        
$r->addReductionFunctionSymbolmap(0'$text');
        
$r->setReductionFunction($identityFunction);
        
$r $this->addContextFreeRule('<quoted-text-part>','""');
        
$r->addReductionFunctionSymbolmap(0'$text');
        
$r->setReductionFunction($identityFunction);
        
$r $this->addContextFreeRule('<rule-name>',        '<unquoted-text>');
        
$r->addReductionFunctionSymbolmap(0'$text');
        
$r->setReductionFunction($identityFunction);
        
$r $this->addContextFreeRule('<unquoted-text>',             '<text-terminal>');
        
$r->addReductionFunctionSymbolmap(0'$text');
        
$r->setReductionFunction($identityFunction);
        
$r $this->addContextFreeRule('<unquoted-text>',             '<quoted-character>');
        
$r->addReductionFunctionSymbolmap(0'$text');
        
$r->setReductionFunction(<<<EOS
\$result = \$text->getValue();
\$result = \$result[1];
EOS
        );
        
$r $this->addContextFreeRule('<unquoted-text>',             '<unquoted-text>','<text-terminal>');
        
$r->addReductionFunctionSymbolmap(0'$texta');
        
$r->addReductionFunctionSymbolmap(1'$textb');
        
$r->setReductionFunction($concatenateFunction);
        
$r $this->addContextFreeRule('<unquoted-text>',             '<unquoted-text>','<quoted-character>');
        
$r->addReductionFunctionSymbolmap(0'$texta');
        
$r->addReductionFunctionSymbolmap(1'$textb');
        
$r->setReductionFunction(<<<EOS
\$result = \$textb->getValue();
\$result = \$texta . \$result[1];
EOS
        );
        
$r $this->addContextFreeRule('<reduction-code>',            "<<<"'<EOL>''<reduction-code-text>''<EOL>'">>>"'<line-end>');
        
$r->addReductionFunctionSymbolmap(2'$code');
        
$r->setReductionFunction('$result = $code->getValue();');
        
$r $this->addContextFreeRule('<reduction-code>');
        
$r $this->addContextFreeRule('<reduction-code-text>',       '<reduction-code-text-part>');
        
$r->addReductionFunctionSymbolmap(0'$part');
        
$r->setReductionFunction($identityFunction);
        
$r $this->addContextFreeRule('<reduction-code-text>',       '<reduction-code-text>''<reduction-code-text-part>');
        
$r->addReductionFunctionSymbolmap(0'$text');
        
$r->addReductionFunctionSymbolmap(1'$part');
        
$r->setReductionFunction($concatenateFunction);
        
$r $this->addContextFreeRule('<reduction-code-text-part>',  '<EOL>');
        
$r->addReductionFunctionSymbolmap(0'$eol');
        
$r->setReductionFunction($identityFunction);
        
$r->setPriority(-1);
        
$r $this->addContextFreeRule('<reduction-code-text-part>',  '>>>');
        
$r->addReductionFunctionSymbolmap(0'$part');
        
$r->setReductionFunction($identityFunction);
        
$r $this->addContextFreeRule('<reduction-code-text-part>',  '<<<');
        
$r->addReductionFunctionSymbolmap(0'$part');
        
$r->setReductionFunction($identityFunction);
        
$r $this->addContextFreeRule('<reduction-code-text-part>',  '"');
        
$r->addReductionFunctionSymbolmap(0'$part');
        
$r->setReductionFunction($identityFunction);
        
$r $this->addContextFreeRule('<reduction-code-text-part>',  "'");
        
$r->addReductionFunctionSymbolmap(0'$part');
        
$r->setReductionFunction($identityFunction);
        
$r $this->addContextFreeRule('<reduction-code-text-part>',  '(');
        
$r->addReductionFunctionSymbolmap(0'$part');
        
$r->setReductionFunction($identityFunction);
        
$r $this->addContextFreeRule('<reduction-code-text-part>',  ')');
        
$r->addReductionFunctionSymbolmap(0'$part');
        
$r->setReductionFunction($identityFunction);
        
$r $this->addContextFreeRule('<reduction-code-text-part>',  '<');
        
$r->addReductionFunctionSymbolmap(0'$part');
        
$r->setReductionFunction($identityFunction);
        
$r $this->addContextFreeRule('<reduction-code-text-part>',  '>');
        
$r->addReductionFunctionSymbolmap(0'$part');
        
$r->setReductionFunction($identityFunction);
        
$r $this->addContextFreeRule('<reduction-code-text-part>',  '::=');
        
$r->addReductionFunctionSymbolmap(0'$part');
        
$r->setReductionFunction($identityFunction);
        
$r $this->addContextFreeRule('<reduction-code-text-part>',  ' ');
        
$r->addReductionFunctionSymbolmap(0'$part');
        
$r->setReductionFunction($identityFunction);
        
$r $this->addContextFreeRule('<reduction-code-text-part>',  '""');
        
$r->addReductionFunctionSymbolmap(0'$part');
        
$r->setReductionFunction($identityFunction);
        
$r $this->addContextFreeRule('<reduction-code-text-part>',  '|');
        
$r->addReductionFunctionSymbolmap(0'$part');
        
$r->setReductionFunction($identityFunction);
        
$r $this->addContextFreeRule('<reduction-code-text-part>',  '<text-terminal>');
        
$r->addReductionFunctionSymbolmap(0'$part');
        
$r->setReductionFunction($identityFunction);
        
$r $this->addContextFreeRule('<reduction-code-text-part>',  '<quoted-character>');
        
$r->addReductionFunctionSymbolmap(0'$part');
        
$r->setReductionFunction($identityFunction);
    }
}
?>