public void ExecuteReduces(GlrStep step, GlrStackItem stackTop) { var inputTerm = step.Input.Term; GlrParserAction actionInfo; if (!stackTop.State.GlrActions.TryGetValue(inputTerm, out actionInfo)) { step.Discarded.Add(stackTop); return; } // if this top is elligible for shift on input, add it to step.ShiftBases if (actionInfo.Shifts.Count > 0) step.ShiftBases.Add(stackTop); foreach (var reduceItem in actionInfo.Reduces) { if (!reduceItem.Lookaheads.Contains(inputTerm)) continue; //execute reduce var prod = reduceItem.Core.Production; var prodLen = prod.RValues.Count; var sliceBuffer = new GlrStackSlice(prodLen); foreach (var slice in EnumerateTopStackSlices(stackTop, sliceBuffer)) { // the slice contains top stack elements, we can now try to reduce var newNode = prod.ExecuteReduce(slice.Items.Select(el => el.ParseNode)); //Shift over newNode from all stack elements preceding reduced segment foreach (var prev in sliceBuffer.PrecedingItems) { GlrParserAction actionFromPrev; if (!prev.State.GlrActions.TryGetValue(newNode.Term, out actionFromPrev) || actionFromPrev.Shifts.Count == 0) // TODO: possible it never happens in LALR, investigate! continue; // no shifts over newNode; // find next state after shifting node var stateAfterShift = actionFromPrev.Shifts[0].ShiftedItem.State; // do not check here if this state is compatible with input; if not it will be discarded inside the ExeduceReduces call that follows var itemAfterShift = new GlrStackItem(stateAfterShift, newNode, prev); ExecuteReduces(step, itemAfterShift); }// foreach prev }//foreach path }//foreach reduce }//method
// Goes recursively, decreasing currentLevel until 0. // Enumerator returns the same object for every yield, and it is the same object as its parameter 'path'. // We do it to reuse a single StackPath instance in all iterations private IEnumerable<GlrStackSlice> EnumerateTopStackSlices(GlrStackItem current, int currentLevel, GlrStackSlice path) { path.Items[currentLevel] = current; if (currentLevel == 0) { // we reach the start of the path path.PrecedingItems = current.Previous; yield return path; } else { foreach (var prev in current.Previous) foreach (var subPath in EnumerateTopStackSlices(prev, currentLevel - 1, path)) { yield return path; //pass path from child } } }
public GlrStackItem(ParserState state, ParseTreeNode parseNode, GlrStackItem previous) { State = state; ParseNode = parseNode; Previous.Add(previous); }
private IEnumerable<GlrStackSlice> EnumerateTopStackSlices(GlrStackItem current, GlrStackSlice slice) { if (slice.Length == 0) { // special case, no child elements - empty production; we generate a single path consisting of 0 elements and PrecedingItems with only 'current' element. // Note that PrecedingItems are those items (states) that will be used as bases (popped states) // from which we will shift over created non-terminal. In case of empty production the base is the top element itself. slice.PrecedingItems = new List<GlrStackItem>(); slice.PrecedingItems.Add(current); return new GlrStackSlice[] {slice}; } else return EnumerateTopStackSlices(current, slice.Length - 1, slice); }