/// <summary> /// Creates a new LRItem with the given dot index from the given other item. /// </summary> /// <param name="dotIndex"></param> /// <param name="otherItem"></param> public LRItem(int dotIndex, LRItem <T> otherItem) { LeftHandSide = otherItem.LeftHandSide; ProductionElements = otherItem.ProductionElements; DotIndex = dotIndex; LookaheadElement = otherItem.LookaheadElement; }
/// <summary> /// Gets a collection of Terminal elements that can appear after the element that is after the dot of the item. /// </summary> /// <example> /// With Grammar: /// S -> E /// E -> T /// E -> (E) /// T -> n /// T -> + T /// T -> T + n /// Follow(S -> •E) : {$} /// Follow(T -> •+ T) : {'+', 'n'} /// Follow(T -> •n, ')') : {')'} /// </example> /// <param name="nonTerminal"></param> /// <returns></returns> public IEnumerable <Terminal <T> > Follow(LRItem <T> item) { //Follow(item) is First(b) where item is: //A -> a•Eb GrammarElement <T> element = item.GetNextElement(1); if (element != null) { //if(item.LookaheadElement != null && item.GetNextElement(2) == null) //{ // //if it is, then include the lookahead of item in the follow set. // return First(element).Concat(new[] { item.LookaheadElement }); //} ////If the element is not the last element in the production. //else //{ return(First(element)); //} } if (item.LookaheadElement == null) { return new[] { EndOfInputElement } } ; return(new[] { item.LookaheadElement }); }
/// <summary> /// Returns whether the value(s) contained by the given other LRItem equals this object. /// </summary> /// <param name="other"></param> /// <returns></returns> public bool Equals(LRItem <T> other) { if (LookaheadElement != null && other.LookaheadElement != null) { return(LeftHandSide.Equals(other.LeftHandSide) && DotIndex == other.DotIndex && ProductionElements.SequenceEqual(other.ProductionElements) && LookaheadElement.Equals(other.LookaheadElement)); } return(LeftHandSide.Equals(other.LeftHandSide) && DotIndex == other.DotIndex && ProductionElements.SequenceEqual(other.ProductionElements)); }
/// <summary> /// Gets the LR(1) Closure of the given item. /// </summary> /// <param name="item"></param> /// <returns></returns> public IEnumerable <LRItem <T> > LR1Closure(LRItem <T> item) { if (closures.ContainsKey(item)) { return(closures[item]); } IEnumerable <LRItem <T> > closure = Lr1Closure(item, new HashSet <LRItem <T> >()); closures.Add(item, closure); return(closure); }
/// <summary> /// Creates the first state. /// </summary> /// <returns></returns> public IEnumerable <LRItem <T> > CreateFirstState() { //get the starting item, S' -> S, $ var startingItem = new LRItem <T>(0, Productions[0], EndOfInputElement); var firstState = new List <LRItem <T> >(); //add S' -> S, $ to the first state firstState.Add(startingItem); //get the rest of the first state firstState.AddRange(Lr1Closure(startingItem, new HashSet <LRItem <T> >())); return(firstState); }
/// <summary> /// Gets the LR(1) Closure of the given item, using currentItems to filter out duplicates. /// </summary> /// <param name="item"></param> /// <param name="currentItems"></param> /// <returns></returns> private IEnumerable <LRItem <T> > Lr1Closure(LRItem <T> item, HashSet <LRItem <T> > currentItems) { //<LRItem<T>> items; //items = new List<LRItem<T>>(); //add the current items //if (currentItems != null) //{ // items.AddRange(currentItems); //} GrammarElement <T> nextElement; //if the next element is a non terminal if ((nextElement = item.GetNextElement()) is NonTerminal <T> ) { //get all of the productions whose LHS equals the next element IEnumerable <Production <T> > productions = Productions.Where(a => a.NonTerminal.Equals(nextElement)); //for each of the possible following elements of item foreach (Terminal <T> l in Follow(item, currentItems)) { foreach (Production <T> p in productions) { //create a new item from the production var newItem = new LRItem <T>(0, p); //with a lookahead element of l from Follow(item) newItem.LookaheadElement = l; //if the item is not already contained if (currentItems.Add(newItem)) { //add the new item(but make sure it is not a duplicate //items.Add(newItem); //add the LR1 Closure of the new item Lr1Closure(newItem, currentItems); } } } } return(currentItems.Distinct()); }
/// <summary> /// Gets a collection of Terminal elements that can appear after the element that is after the dot of the item. /// </summary> /// <example> /// With Grammar: /// S -> E /// E -> T /// E -> (E) /// T -> n /// T -> + T /// T -> T + n /// Follow(S -> •E) : {$} /// Follow(T -> •+ T) : {'+', 'n'} /// Follow(T -> •n, ')') : {')'} /// </example> /// <param name="nonTerminal"></param> /// <returns></returns> private IEnumerable <Terminal <T> > Follow(LRItem <T> item, IEnumerable <LRItem <T> > totalItems) { //Follow(item) is First(b) where item is: //A -> a•Eb GrammarElement <T> element = item.GetNextElement(1); if (element != null) { if (element is NonTerminal <T> ) { var firstSet = new List <Terminal <T> >(); //if the element has a production with no derived elements and the element is at the end of the current item's production, then //add the lookahead of the given item. //if there is any production of the current element that has no derived elements if (Productions.Any(a => a.NonTerminal.Equals(element) && a.DerivedElements.Count == 0)) { //if the current element is the end of the current item's production if (item.GetNextElement(2) == null) { firstSet.Add(item.LookaheadElement ?? EndOfInputElement); } } //select the lookahead element or end of input element for each item in the previous set //List<Terminal<T>> firstSet = new List<Terminal<T>>(items.Select(a => a.LookaheadElement == null ? EndOfInputElement : a.LookaheadElement)); //add the rest of the first set. firstSet.AddRange(First(element)); return(firstSet); } return(First(element)); } if (item.LookaheadElement == null) { return new[] { EndOfInputElement } } ; return(new[] { item.LookaheadElement }); }
/// <summary> /// Gets the collection of terminal elements that can appear as the first element /// after the dot in the given LR Item. If next element, named T, is a terminal, then First(item) = {T} /// </summary> /// <example> /// With Grammar: /// S -> A /// A -> A + B /// A -> a /// B -> b /// First(S -> •A): {'a'} /// First(A -> •A + B): {'a'} /// First(A -> A • + B): {'+'} /// First(B -> b): {'b'} /// </example> /// <param name="item"></param> /// <returns></returns> public IEnumerable <Terminal <T> > First(LRItem <T> item) { //get the next element and evaluate if it is a terminal or non terminal GrammarElement <T> nextElement = item.GetNextElement(); //if nextElement is a Terminal then return {T} if (nextElement is Terminal <T> ) { return new[] { (Terminal <T>)nextElement } } ; //otherwise find all of the productions that have nextElement on the LHS. Production <T> production = Productions.First(p => p.NonTerminal == item.LeftHandSide && p.DerivedElements.SequenceEqual(item.ProductionElements)); var firstElements = new List <Terminal <T> >(); if (production.DerivedElements.Count > 0) { if (!production.DerivedElements[item.DotIndex].Equals(production.NonTerminal)) { GrammarElement <T> productionItem = production.GetElement(item.DotIndex); if (productionItem != null) { //if it is a Terminal add to first elements if (productionItem is Terminal <T> ) { firstElements.Add((Terminal <T>)productionItem); } //otherwise add First(new LRItem(production)) of all of the productions where the LHS == productionItem else { foreach (LRItem <T> i in Productions.Where(p => p.NonTerminal.Equals(productionItem)).Select(p => new LRItem <T>(0, p))) { firstElements.AddRange(First(i)); } } } } } return(firstElements.Distinct()); }
/// <summary> /// Gets a collection of LR(0) items that can be derived from the given item. /// </summary> /// <param name="item"></param> /// <param name="currentItems"></param> /// <returns></returns> private IEnumerable <LRItem <T> > Closure(LRItem <T> item, IEnumerable <LRItem <T> > currentItems) { List <LRItem <T> > items; items = new List <LRItem <T> >(); if (currentItems == null) { items.Add(item); } //if the next item is a non terminal, get all of the productions of that non terminal if (item.GetNextElement() is NonTerminal <T> ) { IEnumerable <Production <T> > productions = Productions.Where(a => a.NonTerminal.Equals(item.GetNextElement())); //remove duplicate productions if (currentItems != null) { productions = productions.Where(a => !currentItems.Any(i => i.LeftHandSide.Equals(a.NonTerminal))); } //add the productions as LRItems items.AddRange(productions.Select(a => new LRItem <T>(0, a))); //add the Closure of each found production foreach (Production <T> p in productions) { if (p.DerivedElements[0] is NonTerminal <T> ) { items.AddRange(Closure(new LRItem <T>(0, p), items)); } } } return(items.Distinct()); }
/// <summary> /// Gets a collection of LR(0) items that can be derived from the given item. /// </summary> /// <param name="item"></param> /// <param name="currentItems"></param> /// <returns></returns> public IEnumerable <LRItem <T> > Closure(LRItem <T> item) { return(Closure(item, null)); }