/** * Compute the closure of the configuration list. * * This calculates all of the possible continuations of * each configuration, ensuring that each state accounts * for every configuration that could arrive at that state. */ public static function Configlist_closure(PHP_ParserGenerator_Data $lemp) { for ($cfp = self::$current; $cfp; $cfp = $cfp->next) { $rp = $cfp->rp; $dot = $cfp->dot; if ($dot >= $rp->nrhs) { continue; } $sp = $rp->rhs[$dot]; if ($sp->type == PHP_ParserGenerator_Symbol::NONTERMINAL) { if ($sp->rule === 0 && $sp !== $lemp->errsym) { PHP_ParserGenerator::ErrorMsg($lemp->filename, $rp->line, "Nonterminal \"%s\" has no rules.", $sp->name); $lemp->errorcnt++; } for ($newrp = $sp->rule; $newrp; $newrp = $newrp->nextlhs) { $newcfp = self::Configlist_add($newrp, 0); for ($i = $dot + 1; $i < $rp->nrhs; $i++) { $xsp = $rp->rhs[$i]; if ($xsp->type == PHP_ParserGenerator_Symbol::TERMINAL) { $newcfp->fws[$xsp->index] = 1; break; } elseif ($xsp->type == PHP_ParserGenerator_Symbol::MULTITERMINAL) { for ($k = 0; $k < $xsp->nsubsym; $k++) { $newcfp->fws[$xsp->subsym[$k]->index] = 1; } break; } else { $a = array_diff_key($xsp->firstset, $newcfp->fws); $newcfp->fws += $a; if ($xsp->lambda === false) { break; } } } if ($i == $rp->nrhs) { PHP_ParserGenerator_PropagationLink::Plink_add($cfp->fplp, $newcfp); } } } } }
/** * zCode is a string that is the action associated with a rule. Expand * the symbols in this string so that the refer to elements of the parser * stack. */ function translate_code(PHP_ParserGenerator_Rule $rp) { $lhsused = 0; /* True if the LHS element has been used */ $used = array(); /* True for each RHS element which is used */ for ($i = 0; $i < $rp->nrhs; $i++) { $used[$i] = 0; } $this->append_str('', 0); for ($i = 0; $i < strlen($rp->code); $i++) { $cp = $rp->code[$i]; if (preg_match('/[A-Za-z]/', $cp) && ($i === 0 || !preg_match('/[A-Za-z0-9_]/', $rp->code[$i - 1]))) { //*xp = 0; // previous line is in essence a temporary substr, so // we will simulate it $test = substr($rp->code, $i); preg_match('/[A-Za-z0-9_]+/', $test, $matches); $tempcp = $matches[0]; $j = strlen($tempcp) + $i; if ($rp->lhsalias && $tempcp == $rp->lhsalias) { $this->append_str("\$this->_retvalue", 0); $cp = $rp->code[$j]; $i = $j; $lhsused = 1; } else { for ($ii = 0; $ii < $rp->nrhs; $ii++) { if ($rp->rhsalias[$ii] && $tempcp == $rp->rhsalias[$ii]) { if ($ii !== 0 && $rp->code[$ii - 1] == '@') { /* If the argument is of the form @X then substitute ** the token number of X, not the value of X */ $this->append_str("\$this->yystack[\$this->yyidx + " . ($ii - $rp->nrhs + 1) . "]->major", -1); } else { $sp = $rp->rhs[$ii]; if ($sp->type == PHP_ParserGenerator_Symbol::MULTITERMINAL) { $dtnum = $sp->subsym[0]->dtnum; } else { $dtnum = $sp->dtnum; } $this->append_str("\$this->yystack[\$this->yyidx + " . ($ii - $rp->nrhs + 1) . "]->minor", 0); } $cp = $rp->code[$j]; $i = $j; $used[$ii] = 1; break; } } } } $this->append_str($cp, 1); } /* End loop */ /* Check to make sure the LHS has been used */ if ($rp->lhsalias && !$lhsused) { PHP_ParserGenerator::ErrorMsg($this->filename, $rp->ruleline, "Label \"%s\" for \"%s(%s)\" is never used.", $rp->lhsalias, $rp->lhs->name, $rp->lhsalias); $this->errorcnt++; } /* Generate destructor code for RHS symbols which are not used in the ** reduce code */ for ($i = 0; $i < $rp->nrhs; $i++) { if ($rp->rhsalias[$i] && !isset($used[$i])) { PHP_ParserGenerator::ErrorMsg($this->filename, $rp->ruleline, "Label %s for \"%s(%s)\" is never used.", $rp->rhsalias[$i], $rp->rhs[$i]->name, $rp->rhsalias[$i]); $this->errorcnt++; } elseif ($rp->rhsalias[$i] == 0) { if ($rp->rhs[$i]->type == PHP_ParserGenerator_Symbol::TERMINAL) { $hasdestructor = $this->tokendest != 0; } else { $hasdestructor = $this->vardest !== 0 || $rp->rhs[$i]->destructor !== 0; } if ($hasdestructor) { $this->append_str(" \$this->yy_destructor(" . $rp->rhs[$i]->index . ", \$this->yystack[\$this->yyidx + " . ($i - $rp->nrhs + 1) . "]->minor);\n", 0); } else { /* No destructor defined for this term */ } } } $cp = $this->append_str('', 0); $rp->code = $cp; }
/** * Parse a single token * @param string token */ function parseonetoken($token) { $x = $token; $this->a = 0; // for referencing in WAITING_FOR_DECL_KEYWORD if (PHP_ParserGenerator::DEBUG) { printf("%s:%d: Token=[%s] state=%d\n", $this->filename, $this->tokenlineno, $token, $this->state); } switch ($this->state) { case self::INITIALIZE: $this->prevrule = 0; $this->preccounter = 0; $this->firstrule = $this->lastrule = 0; $this->gp->nrule = 0; /* Fall thru to next case */ /* Fall thru to next case */ case self::WAITING_FOR_DECL_OR_RULE: if ($x[0] == '%') { $this->state = self::WAITING_FOR_DECL_KEYWORD; } elseif (preg_match('/[a-z]/', $x[0])) { $this->lhs = PHP_ParserGenerator_Symbol::Symbol_new($x); $this->nrhs = 0; $this->lhsalias = 0; $this->state = self::WAITING_FOR_ARROW; } elseif ($x[0] == '{') { if ($this->prevrule === 0) { PHP_ParserGenerator::ErrorMsg($this->filename, $this->tokenlineno, "There is no prior rule opon which to attach the code\n fragment which begins on this line."); $this->errorcnt++; } elseif ($this->prevrule->code != 0) { PHP_ParserGenerator::ErrorMsg($this->filename, $this->tokenlineno, "Code fragment beginning on this line is not the first \\\n to follow the previous rule."); $this->errorcnt++; } else { $this->prevrule->line = $this->tokenlineno; $this->prevrule->code = substr($x, 1); } } elseif ($x[0] == '[') { $this->state = self::PRECEDENCE_MARK_1; } else { PHP_ParserGenerator::ErrorMsg($this->filename, $this->tokenlineno, "Token \"%s\" should be either \"%%\" or a nonterminal name.", $x); $this->errorcnt++; } break; case self::PRECEDENCE_MARK_1: if (!preg_match('/[A-Z]/', $x[0])) { PHP_ParserGenerator::ErrorMsg($this->filename, $this->tokenlineno, "The precedence symbol must be a terminal."); $this->errorcnt++; } elseif ($this->prevrule === 0) { PHP_ParserGenerator::ErrorMsg($this->filename, $this->tokenlineno, "There is no prior rule to assign precedence \"[%s]\".", $x); $this->errorcnt++; } elseif ($this->prevrule->precsym != 0) { PHP_ParserGenerator::ErrorMsg($this->filename, $this->tokenlineno, "Precedence mark on this line is not the first to follow the previous rule."); $this->errorcnt++; } else { $this->prevrule->precsym = PHP_ParserGenerator_Symbol::Symbol_new($x); } $this->state = self::PRECEDENCE_MARK_2; break; case self::PRECEDENCE_MARK_2: if ($x[0] != ']') { PHP_ParserGenerator::ErrorMsg($this->filename, $this->tokenlineno, "Missing \"]\" on precedence mark."); $this->errorcnt++; } $this->state = self::WAITING_FOR_DECL_OR_RULE; break; case self::WAITING_FOR_ARROW: if ($x[0] == ':' && $x[1] == ':' && $x[2] == '=') { $this->state = self::IN_RHS; } elseif ($x[0] == '(') { $this->state = self::LHS_ALIAS_1; } else { PHP_ParserGenerator::ErrorMsg($this->filename, $this->tokenlineno, "Expected to see a \":\" following the LHS symbol \"%s\".", $this->lhs->name); $this->errorcnt++; $this->state = self::RESYNC_AFTER_RULE_ERROR; } break; case self::LHS_ALIAS_1: if (preg_match('/[A-Za-z]/', $x[0])) { $this->lhsalias = $x; $this->state = self::LHS_ALIAS_2; } else { PHP_ParserGenerator::ErrorMsg($this->filename, $this->tokenlineno, "\"%s\" is not a valid alias for the LHS \"%s\"\n", $x, $this->lhs->name); $this->errorcnt++; $this->state = self::RESYNC_AFTER_RULE_ERROR; } break; case self::LHS_ALIAS_2: if ($x[0] == ')') { $this->state = self::LHS_ALIAS_3; } else { PHP_ParserGenerator::ErrorMsg($this->filename, $this->tokenlineno, "Missing \")\" following LHS alias name \"%s\".", $this->lhsalias); $this->errorcnt++; $this->state = self::RESYNC_AFTER_RULE_ERROR; } break; case self::LHS_ALIAS_3: if ($x == '::=') { $this->state = self::IN_RHS; } else { PHP_ParserGenerator::ErrorMsg($this->filename, $this->tokenlineno, "Missing \"->\" following: \"%s(%s)\".", $this->lhs->name, $this->lhsalias); $this->errorcnt++; $this->state = self::RESYNC_AFTER_RULE_ERROR; } break; case self::IN_RHS: if ($x[0] == '.') { $rp = new PHP_ParserGenerator_Rule(); $rp->ruleline = $this->tokenlineno; for ($i = 0; $i < $this->nrhs; $i++) { $rp->rhs[$i] = $this->rhs[$i]; $rp->rhsalias[$i] = $this->alias[$i]; } $rp->lhs = $this->lhs; $rp->lhsalias = $this->lhsalias; $rp->nrhs = $this->nrhs; $rp->code = 0; $rp->precsym = 0; $rp->index = $this->gp->nrule++; $rp->nextlhs = $rp->lhs->rule; $rp->lhs->rule = $rp; $rp->next = 0; if ($this->firstrule === 0) { $this->firstrule = $this->lastrule = $rp; } else { $this->lastrule->next = $rp; $this->lastrule = $rp; } $this->prevrule = $rp; $this->state = self::WAITING_FOR_DECL_OR_RULE; } elseif (preg_match('/[a-zA-Z]/', $x[0])) { if ($this->nrhs >= PHP_ParserGenerator::MAXRHS) { PHP_ParserGenerator::ErrorMsg($this->filename, $this->tokenlineno, "Too many symbols on RHS or rule beginning at \"%s\".", $x); $this->errorcnt++; $this->state = self::RESYNC_AFTER_RULE_ERROR; } else { if (isset($this->rhs[$this->nrhs - 1])) { $msp = $this->rhs[$this->nrhs - 1]; if ($msp->type == PHP_ParserGenerator_Symbol::MULTITERMINAL) { $inf = array_reduce($msp->subsym, array($this, '_printmulti'), ''); PHP_ParserGenerator::ErrorMsg($this->filename, $this->tokenlineno, 'WARNING: symbol ' . $x . ' will not' . ' be part of previous multiterminal %s', substr($inf, 0, strlen($inf) - 1)); } } $this->rhs[$this->nrhs] = PHP_ParserGenerator_Symbol::Symbol_new($x); $this->alias[$this->nrhs] = 0; $this->nrhs++; } } elseif (($x[0] == '|' || $x[0] == '/') && $this->nrhs > 0) { $msp = $this->rhs[$this->nrhs - 1]; if ($msp->type != PHP_ParserGenerator_Symbol::MULTITERMINAL) { $origsp = $msp; $msp = new PHP_ParserGenerator_Symbol(); $msp->type = PHP_ParserGenerator_Symbol::MULTITERMINAL; $msp->nsubsym = 1; $msp->subsym = array($origsp); $msp->name = $origsp->name; $this->rhs[$this->nrhs - 1] = $msp; } $msp->nsubsym++; $msp->subsym[$msp->nsubsym - 1] = PHP_ParserGenerator_Symbol::Symbol_new(substr($x, 1)); if (preg_match('/[a-z]/', $x[1]) || preg_match('/[a-z]/', $msp->subsym[0]->name[0])) { PHP_ParserGenerator::ErrorMsg($this->filename, $this->tokenlineno, "Cannot form a compound containing a non-terminal"); $this->errorcnt++; } } elseif ($x[0] == '(' && $this->nrhs > 0) { $this->state = self::RHS_ALIAS_1; } else { PHP_ParserGenerator::ErrorMsg($this->filename, $this->tokenlineno, "Illegal character on RHS of rule: \"%s\".", $x); $this->errorcnt++; $this->state = self::RESYNC_AFTER_RULE_ERROR; } break; case self::RHS_ALIAS_1: if (preg_match('/[A-Za-z]/', $x[0])) { $this->alias[$this->nrhs - 1] = $x; $this->state = self::RHS_ALIAS_2; } else { PHP_ParserGenerator::ErrorMsg($this->filename, $this->tokenlineno, "\"%s\" is not a valid alias for the RHS symbol \"%s\"\n", $x, $this->rhs[$this->nrhs - 1]->name); $this->errorcnt++; $this->state = self::RESYNC_AFTER_RULE_ERROR; } break; case self::RHS_ALIAS_2: if ($x[0] == ')') { $this->state = self::IN_RHS; } else { PHP_ParserGenerator::ErrorMsg($this->filename, $this->tokenlineno, "Missing \")\" following LHS alias name \"%s\".", $this->lhsalias); $this->errorcnt++; $this->state = self::RESYNC_AFTER_RULE_ERROR; } break; case self::WAITING_FOR_DECL_KEYWORD: if (preg_match('/[A-Za-z]/', $x[0])) { $this->declkeyword = $x; $this->declargslot =& $this->a; $this->decllnslot =& $this->a; $this->state = self::WAITING_FOR_DECL_ARG; if ('name' == $x) { $this->declargslot =& $this->gp->name; } elseif ('include' == $x) { $this->declargslot =& $this->gp->include_code; $this->decllnslot =& $this->gp->includeln; } elseif ('include_class' == $x) { $this->declargslot =& $this->gp->include_classcode; $this->decllnslot =& $this->gp->include_classln; } elseif ('declare_class' == $x) { $this->declargslot =& $this->gp->declare_classcode; $this->decllnslot =& $this->gp->declare_classln; } elseif ('code' == $x) { $this->declargslot =& $this->gp->extracode; $this->decllnslot =& $this->gp->extracodeln; } elseif ('token_destructor' == $x) { $this->declargslot =& $this->gp->tokendest; $this->decllnslot =& $this->gp->tokendestln; } elseif ('default_destructor' == $x) { $this->declargslot =& $this->gp->vardest; $this->decllnslot =& $this->gp->vardestln; } elseif ('token_prefix' == $x) { $this->declargslot =& $this->gp->tokenprefix; } elseif ('syntax_error' == $x) { $this->declargslot =& $this->gp->error; $this->decllnslot =& $this->gp->errorln; } elseif ('parse_accept' == $x) { $this->declargslot =& $this->gp->accept; $this->decllnslot =& $this->gp->acceptln; } elseif ('parse_failure' == $x) { $this->declargslot =& $this->gp->failure; $this->decllnslot =& $this->gp->failureln; } elseif ('stack_overflow' == $x) { $this->declargslot =& $this->gp->overflow; $this->decllnslot =& $this->gp->overflowln; } elseif ('token_type' == $x) { $this->declargslot =& $this->gp->tokentype; } elseif ('default_type' == $x) { $this->declargslot =& $this->gp->vartype; } elseif ('stack_size' == $x) { $this->declargslot =& $this->gp->stacksize; } elseif ('start_symbol' == $x) { $this->declargslot =& $this->gp->start; } elseif ('left' == $x) { $this->preccounter++; $this->declassoc = PHP_ParserGenerator_Symbol::LEFT; $this->state = self::WAITING_FOR_PRECEDENCE_SYMBOL; } elseif ('right' == $x) { $this->preccounter++; $this->declassoc = PHP_ParserGenerator_Symbol::RIGHT; $this->state = self::WAITING_FOR_PRECEDENCE_SYMBOL; } elseif ('nonassoc' == $x) { $this->preccounter++; $this->declassoc = PHP_ParserGenerator_Symbol::NONE; $this->state = self::WAITING_FOR_PRECEDENCE_SYMBOL; } elseif ('destructor' == $x) { $this->state = self::WAITING_FOR_DESTRUCTOR_SYMBOL; } elseif ('type' == $x) { $this->state = self::WAITING_FOR_DATATYPE_SYMBOL; } elseif ('fallback' == $x) { $this->fallback = 0; $this->state = self::WAITING_FOR_FALLBACK_ID; } else { PHP_ParserGenerator::ErrorMsg($this->filename, $this->tokenlineno, "Unknown declaration keyword: \"%%%s\".", $x); $this->errorcnt++; $this->state = self::RESYNC_AFTER_DECL_ERROR; } } else { PHP_ParserGenerator::ErrorMsg($this->filename, $this->tokenlineno, "Illegal declaration keyword: \"%s\".", $x); $this->errorcnt++; $this->state = self::RESYNC_AFTER_DECL_ERROR; } break; case self::WAITING_FOR_DESTRUCTOR_SYMBOL: if (!preg_match('/[A-Za-z]/', $x[0])) { PHP_ParserGenerator::ErrorMsg($this->filename, $this->tokenlineno, "Symbol name missing after %destructor keyword"); $this->errorcnt++; $this->state = self::RESYNC_AFTER_DECL_ERROR; } else { $sp = PHP_ParserGenerator_Symbol::Symbol_new($x); $this->declargslot =& $sp->destructor; $this->decllnslot =& $sp->destructorln; $this->state = self::WAITING_FOR_DECL_ARG; } break; case self::WAITING_FOR_DATATYPE_SYMBOL: if (!preg_match('/[A-Za-z]/', $x[0])) { PHP_ParserGenerator::ErrorMsg($this->filename, $this->tokenlineno, "Symbol name missing after %destructor keyword"); $this->errorcnt++; $this->state = self::RESYNC_AFTER_DECL_ERROR; } else { $sp = PHP_ParserGenerator_Symbol::Symbol_new($x); $this->declargslot =& $sp->datatype; $this->state = self::WAITING_FOR_DECL_ARG; } break; case self::WAITING_FOR_PRECEDENCE_SYMBOL: if ($x[0] == '.') { $this->state = self::WAITING_FOR_DECL_OR_RULE; } elseif (preg_match('/[A-Z]/', $x[0])) { $sp = PHP_ParserGenerator_Symbol::Symbol_new($x); if ($sp->prec >= 0) { PHP_ParserGenerator::ErrorMsg($this->filename, $this->tokenlineno, "Symbol \"%s\" has already been given a precedence.", $x); $this->errorcnt++; } else { $sp->prec = $this->preccounter; $sp->assoc = $this->declassoc; } } else { PHP_ParserGenerator::ErrorMsg($this->filename, $this->tokenlineno, "Can't assign a precedence to \"%s\".", $x); $this->errorcnt++; } break; case self::WAITING_FOR_DECL_ARG: if (preg_match('/[A-Za-z0-9{"]/', $x[0])) { if ($this->declargslot != 0) { PHP_ParserGenerator::ErrorMsg($this->filename, $this->tokenlineno, "The argument \"%s\" to declaration \"%%%s\" is not the first.", $x[0] == '"' ? substr($x, 1) : $x, $this->declkeyword); $this->errorcnt++; $this->state = self::RESYNC_AFTER_DECL_ERROR; } else { $this->declargslot = $x[0] == '"' || $x[0] == '{' ? substr($x, 1) : $x; $this->a = 1; if (!$this->decllnslot) { $this->decllnslot = $this->tokenlineno; } $this->state = self::WAITING_FOR_DECL_OR_RULE; } } else { PHP_ParserGenerator::ErrorMsg($this->filename, $this->tokenlineno, "Illegal argument to %%%s: %s", $this->declkeyword, $x); $this->errorcnt++; $this->state = self::RESYNC_AFTER_DECL_ERROR; } break; case self::WAITING_FOR_FALLBACK_ID: if ($x[0] == '.') { $this->state = self::WAITING_FOR_DECL_OR_RULE; } elseif (!preg_match('/[A-Z]/', $x[0])) { PHP_ParserGenerator::ErrorMsg($this->filename, $this->tokenlineno, "%%fallback argument \"%s\" should be a token", $x); $this->errorcnt++; } else { $sp = PHP_ParserGenerator_Symbol::Symbol_new($x); if ($this->fallback === 0) { $this->fallback = $sp; } elseif (is_object($sp->fallback)) { PHP_ParserGenerator::ErrorMsg($this->filename, $this->tokenlineno, "More than one fallback assigned to token %s", $x); $this->errorcnt++; } else { $sp->fallback = $this->fallback; $this->gp->has_fallback = 1; } } break; case self::RESYNC_AFTER_RULE_ERROR: /* if ($x[0] == '.') $this->state = self::WAITING_FOR_DECL_OR_RULE; ** break; */ /* if ($x[0] == '.') $this->state = self::WAITING_FOR_DECL_OR_RULE; ** break; */ case self::RESYNC_AFTER_DECL_ERROR: if ($x[0] == '.') { $this->state = self::WAITING_FOR_DECL_OR_RULE; } if ($x[0] == '%') { $this->state = self::WAITING_FOR_DECL_KEYWORD; } break; } }