private function &CompileExpression($tree, $rule_name = '', $omit_override = null, $non_override = null)
 {
     $omit = $omit_override === null ? $tree->node(0, 0) !== false ? $tree->index(0, 0) == 1 : false : $omit_override;
     $non = $non_override === null ? $tree->node(0, 1) !== false ? $tree->index(0, 1) == 1 : false : $non_override;
     $expression_type = $tree->nodes[1]->index;
     if ($rule_name === '') {
         switch ($expression_type) {
             case 1:
                 // any
                 $object = new ParseAny($omit, $non);
                 break;
             case 2:
                 // end
                 $object = new ParseEOS($omit, $non);
                 break;
             case 3:
                 // begin
                 $object = new ParseBOS($omit, $non);
                 break;
             case 4:
                 // set
                 $object = new ParseSet($omit, $non);
                 break;
             case 5:
                 // not
                 $object = new ParseNot($omit, $non);
                 break;
             case 6:
                 // opt
                 $object = new ParseOptional($omit, $non);
                 break;
             case 7:
                 // and
                 $object = new ParseAnd($omit, $non);
                 break;
             case 8:
                 // or
                 $object = new ParseOr($omit, $non);
                 break;
             case 9:
                 // list
                 $object = new ParseList($omit, $non);
                 break;
             case 10:
                 // literal
                 $object = new ParseText($omit, $non);
                 break;
         }
     } else {
         $object =& $this->rules[$rule_name];
     }
     switch ($expression_type) {
         case 1:
             // any
             break;
         case 2:
             // end
             break;
         case 3:
             // begin
             break;
         case 4:
             // set
             $case = $tree->index(1, 0, 1, 0) == 1;
             $literal_set = '';
             foreach ($tree->node(1, 0, 1, 1, 0)->nodes as $char) {
                 switch ($char->index) {
                     case 1:
                         $literal_set .= '|';
                         break;
                     case 2:
                         $literal_set .= chr($char->nodes[0]->text($this->stream));
                         break;
                     case 3:
                         $literal_set .= $char->nodes[0]->text($this->stream);
                         break;
                 }
             }
             $object->def($case, $literal_set);
             break;
         case 5:
             // not
         // not
         case 6:
             // opt
             $condition =& $this->CompileExpression($tree->node(1, 0));
             $object->def($condition);
             break;
         case 7:
             // and
         // and
         case 8:
             // or
             $object->set = array();
             foreach ($tree->node(1, 0, 1)->nodes as $node) {
                 $object->set[] =& $this->CompileExpression($node);
             }
             $object->length = count($object->set);
             break;
         case 9:
             // list
             $object->condition =& $this->CompileExpression($tree->node(1, 0, 1));
             if ($tree->index(1, 0, 2) == 1) {
                 $object->delimiter =& $this->CompileExpression($tree->node(1, 0, 2, 0, 1));
             } else {
                 $object->delimiter = null;
             }
             if ($tree->index(1, 0, 3) == 1) {
                 $object->terminator =& $this->CompileExpression($tree->node(1, 0, 3, 0, 1));
             } else {
                 $object->terminator = null;
             }
             $object->min = $tree->index(1, 0, 4) == 1 ? $tree->text($this->stream, 1, 0, 4, 0, 1) : 1;
             $object->max = $tree->index(1, 0, 5) == 1 ? $tree->text($this->stream, 1, 0, 5, 0, 1) : -1;
             break;
         case 10:
             // literal
             $case = $tree->index(1, 0, 0) == 1;
             $text = $tree->text($this->stream, 1, 0, 1, 0);
             $encoded_text = '';
             foreach ($tree->node(1, 0, 1, 0)->nodes as $char) {
                 switch ($char->index) {
                     case 1:
                         $encoded_text .= '|';
                         break;
                     case 2:
                         $encoded_text .= chr($char->nodes[0]->text($this->stream));
                         break;
                     case 3:
                         $encoded_text .= $char->nodes[0]->text($this->stream);
                         break;
                 }
             }
             switch ($tree->index(1, 0, 1)) {
                 case 1:
                     // escaped
                     $object->def($case, $encoded_text);
                     break;
                 case 2:
                     // non escaped
                     if (array_key_exists($text, $this->rules)) {
                         $object =& $this->rules[$text];
                         $o = $object->omit || $omit;
                         $n = $object->non_consuming || $non;
                         $object =& $this->rules[$text . '_' . ($o ? 'o' : '') . ($n ? 'n' : '')];
                     } else {
                         $object->def($case, $encoded_text);
                     }
                     break;
             }
             break;
     }
     return $object;
 }
 function __construct()
 {
     $and = new ParseText(false, false);
     $and->def(false, "and");
     $or = new ParseText(false, false);
     $or->def(false, "or");
     $set = new ParseText(false, false);
     $set->def(false, "set");
     $not = new ParseText(false, false);
     $not->def(false, "not");
     $opt = new ParseText(false, false);
     $opt->def(false, "opt");
     $any = new ParseText(false, false);
     $any->def(false, "any");
     $eos = new ParseText(false, false);
     $eos->def(false, "eos");
     $list = new ParseText(false, false);
     $list->def(false, "list");
     $min = new ParseText(false, false);
     $min->def(false, "min");
     $max = new ParseText(false, false);
     $max->def(false, "max");
     $del = new ParseText(false, false);
     $del->def(false, "del");
     $until = new ParseText(false, false);
     $until->def(false, "until");
     $non = new ParseText(false, false);
     $non->def(false, "non");
     $omit = new ParseText(false, false);
     $omit->def(false, "omit");
     $case = new ParseText(false, false);
     $case->def(false, "case");
     $end = new ParseText(false, false);
     $end->def(false, "end");
     $pipe = new ParseText(false, false);
     $pipe->def(false, "|");
     $pipe_omit = new ParseText(true, false);
     $pipe_omit->def(false, "|");
     $double_pipe = new ParseAnd(false, false);
     $double_pipe->def($pipe, $pipe_omit);
     $double_pipe_omit = new ParseAnd(true, false);
     $double_pipe_omit->def($pipe, $pipe);
     $space = new ParseSet(false, false);
     $space->def(false, " ");
     $ws = new ParseList(true, false);
     $ws->def($space, null, null, 0);
     $anychar = new ParseAny(false, false);
     $digit = new ParseSet(false, false);
     $digit->def(false, "0123456789");
     $literal_char = new ParseOr(false, false);
     $literal_char->def($double_pipe, $anychar);
     $not_pipe = new ParseNot(false, false);
     $not_pipe->def($pipe);
     $single_pipe = new ParseAnd(true, false);
     $single_pipe->def($pipe, $not_pipe);
     $not_identifier_char = new ParseOr(true, true);
     $not_identifier_char->def($space, $single_pipe);
     $identifier = new ParseList(false, false);
     $identifier->def($literal_char, null, $not_identifier_char);
     $literal_text = new ParseList(false, false);
     $literal_text->def($literal_char, null, $single_pipe, 0);
     $literal = new ParseAnd(false, false);
     $literal->def($double_pipe_omit, $literal_text);
     $case_opt = new ParseOptional(false, false);
     $case_opt->def($case);
     $omit_opt = new ParseOptional(false, false);
     $omit_opt->def($omit);
     $non_opt = new ParseOptional(false, false);
     $non_opt->def($non);
     $omit_non = new ParseAnd(false, false);
     $omit_non->def($omit_opt, $ws, $non_opt, $ws);
     $text = new ParseOr(false, false);
     $text->def($literal, $identifier);
     $text_case = new ParseAnd(false, false);
     $text_case->def($case_opt, $ws, $text);
     $and_ommited_expression = new ParseAnd(false, false);
     $and_expression = new ParseAnd(false, false);
     $or_expression = new ParseAnd(false, false);
     $not_expression = new ParseAnd(false, false);
     $set_expression = new ParseAnd(false, false);
     $opt_expression = new ParseAnd(false, false);
     $list_expression = new ParseAnd(false, false);
     $expression = new ParseOr(false, false);
     $expression->def($any, $eos, $set_expression, $not_expression, $opt_expression, $and_expression, $or_expression, $list_expression, $text_case);
     $full_expression = new ParseAnd(false, false);
     $full_expression->def($omit_non, $expression);
     $expression_list = new ParseList(false, false);
     $expression_list->def($full_expression, $ws);
     $and_ommited_expression->def($expression_list, $ws, $pipe);
     $and_expression->def($and, $ws, $expression_list, $ws, $pipe);
     $or_expression->def($or, $ws, $expression_list, $ws, $pipe);
     $not_expression->def($not, $ws, $expression, $ws, $pipe);
     $set_expression->def($set, $ws, $text_case, $ws, $pipe);
     $opt_expression->def($opt, $ws, $expression, $ws, $pipe);
     $number = new ParseList(false, false);
     $number->def($digit);
     $delimit_clause = new ParseAnd(false, false);
     $delimit_clause->def($ws, $del, $ws, $full_expression);
     $until_clause = new ParseAnd(false, false);
     $until_clause->def($ws, $until, $ws, $full_expression);
     $min_clause = new ParseAnd(false, false);
     $min_clause->def($ws, $min, $ws, $number);
     $max_clause = new ParseAnd(false, false);
     $max_clause->def($ws, $max, $ws, $number);
     $delimit_opt = new ParseOptional(false, false);
     $delimit_opt->def($delimit_clause);
     $until_opt = new ParseOptional(false, false);
     $until_opt->def($until_clause);
     $min_opt = new ParseOptional(false, false);
     $min_opt->def($min_clause);
     $max_opt = new ParseOptional(false, false);
     $max_opt->def($max_clause);
     $list_expression->def($list, $ws, $full_expression, $delimit_opt, $until_opt, $min_opt, $max_opt, $ws, $pipe);
     $end = new ParseEOS(false, false);
     $rule = new ParseAnd(false, false);
     $rule->def($identifier, $ws, $full_expression, $ws, $pipe, $ws);
     $this->rules_parser = new ParseList(false, false);
     $this->rules_parser->def($rule, null);
 }