/** * Creates a new collection of rules. * * You must provide a list of rules, the following formats are accepted: * * #### Rules as arrays * * ``php * [ * 'S -> a A' => function (&$info) { }, * 'A -> b B' => function (&$info) { }, * 'B -> c', // semantic routine is optional * .... * ] * ``` * * #### Rules as a single string * * In this case no semantic routine is given for each rule * * ```php * [ * 'S -> a A', * 'A -> b B', * 'C -> c', * ] * ``` * * #### Rules as a rule objects * * ```php * [ * new Rule('S -> a A', function (&$info) { }), * new Rule('A -> b B', function (&$info) { }), * new Rule('B -> c'), // semantic routine is optional * ] * ``` * * --- * * Of course you can combine formats: * * ```php * [ * 'S -> a A' => function($info) { }, * 'A -> b B', * new Rule('B -> c'), * ] * ``` * * @param array $collection List of rules given as an array * @return void */ public function __construct(array $collection = []) { if (empty($collection)) { return; } $i = 0; foreach ($collection as $index => $rule) { if (!$rule instanceof Rule) { if (is_string($index) && is_callable($rule)) { // 'S -> a' => function() $left = $index; $right = $rule; } elseif (is_integer($index) && is_string($rule)) { // 0 => 'S -> a' $left = $rule; $right = ''; } if (isset($left) && isset($right)) { $rule = new Rule($left, $right); unset($left, $right); } } if (!$rule instanceof Rule) { continue; } array_unshift($this->_variables, $rule->lhs()); if ($i == 0) { $this->_startVariable = $rule->lhs(); } $this->_rules[] = $rule; $i++; } $this->_variables = array_unique($this->_variables); foreach ($this->_rules as $rule) { $rhs = $rule->rhs(); $rhs = explode(' ', $rhs); $rhs = array_diff($rhs, $this->_variables); $this->_terminals = array_merge($this->_terminals, $rhs); } $this->_terminals = array_unique($this->_terminals); }
/** * Calculates the closure set for the given set of rules * * @param \Phparser\Rule\RulesCollection $set Set of rules * @return array */ protected function _closure(RulesCollection $set) { $regex = implode('|', $this->variables()); $first = $this->first(); $hasChanged = true; while ($hasChanged) { $hasChanged = false; foreach ($set as $rule) { $rhs = $rule->rhs(); $result = preg_match('/\\.\\b(' . $regex . ')\\b/', $rhs, $matches); if ($result) { $rightSymbols = end(explode($matches[1], $rhs)); $variable = str_replace('.', '', $matches[1]); foreach ($this->_rules as $r) { $lhs = $r->lhs(); if ($lhs == $variable) { $seq = trim($rightSymbols . ' ' . $rule->lookahead()); foreach ($this->_first($first, $seq) as $lookahead) { $newRHS = '.' . $r->rhs(); $newPro = new Rule("{$variable} -> {$newRHS}"); $newPro->lookahead($lookahead); if ($set->push($newPro)) { $hasChanged = true; } } } } } } } return $set; }