/// \brief Computes the first set for the specified rule (or retrieves the cached version) item_set grammar::first_for_rule(const rule& rule) const { // Return a set containing only the empty item if the rule is 0 items long if (rule.items().size() == 0) { return *m_EpsilonSet; } // Return the first set of the first item in the rule item_set result(this); for (size_t itemId = 0; itemId < rule.items().size(); ++itemId) { // Remove the epsilon item from the set result.erase(an_empty_item_c); // Add the items in the first set for this item result.merge(first(*rule.items()[itemId])); // If the result doesn't contain the empty item, then return it if (!result.contains(an_empty_item_c)) { return result; } } // The empty item is included result.insert(an_empty_item_c); return result; }
/// \brief Updates the follow set cache using the content of a particular rule void grammar::fill_follow(const rule& rule, item_map<item_set>::type& dependencies) const { // Empty rules don't change the follow set for anything if (rule.items().size() == 0) return; // Iterate through the items in this rule for (size_t pos=0; pos<rule.items().size(); ++pos) { // Get the current item const item_container& thisItem = rule.items()[pos]; // Terminal items aren't processed by this call (we don't bother to generate follow sets for them) if (thisItem->type() == item::terminal) continue; // Retrieve the follow set for this item item_set_map::iterator followSet = m_CachedFollowSets.find(thisItem); if (followSet == m_CachedFollowSets.end()) { followSet = m_CachedFollowSets.insert(item_set_map::value_type(thisItem, item_set(this))).first; } // The follow set of this item is the combination of the first sets for all of the following items // If it's at the end, it also includes the follow set for the nonterminal for this rule size_t nextPos = pos+1; for (;nextPos < rule.items().size(); ++nextPos) { // Get this following item const item_container& followingItem = rule.items()[nextPos]; // Get the set FIRST(followingItem) const item_set& firstSet = first(followingItem); // Add to the follow set followSet->second.merge(firstSet); // Finished if the first set doesn't include the empty set if (!firstSet.contains(an_empty_item_c)) break; } // If we reach the end, then we need to include FOLLOW(rule.nonterminal) in the set for FOLLOW(thisItem) if (nextPos >= rule.items().size()) { item_set_map::iterator depend = dependencies.find(thisItem); if (depend == dependencies.end()) { depend = dependencies.insert(item_map<item_set>::type::value_type(thisItem, item_set(this))).first; } depend->second.insert(rule.nonterminal()); } // If this item is an EBNF rule, then we need to process each of its children const ebnf* ebnfItem = thisItem->cast_ebnf(); if (ebnfItem) { for (ebnf::rule_iterator subRule = ebnfItem->first_rule(); subRule != ebnfItem->last_rule(); ++subRule) { fill_follow(**subRule, dependencies); } } } }