/// <summary> /// Initializes the GSS /// </summary> public GSS() { nodeLabels = new BigList <int>(); nodeGenerations = new BigList <GSSGeneration>(); edges = new BigList <GSSEdge>(); edgeGenerations = new BigList <GSSGeneration>(); generation = -1; path0 = new GSSPath(); paths0 = new [] { path0 }; paths = new GSSPath[INIT_PATHS_COUNT]; }
/// <summary> /// Prepares for the forthcoming reduction operations /// </summary> /// <param name="first">The first label</param> /// <param name="path">The path being reduced</param> /// <param name="length">The reduction length</param> public void ReductionPrepare(int first, GSSPath path, int length) { // build the stack if (length > 0) { for (int i = 0; i < length - 1; i++) { stack[i] = path[length - 2 - i]; } stack[length - 1] = first; } // initialize the reduction data cacheNext = 0; handleNext = 0; popCount = 0; }
/// <summary> /// Setups a reusable GSS path with the given length /// </summary> /// <param name="index">The index in the buffer of reusable paths</param> /// <param name="last">The last GLR state in the path</param> /// <param name="length">The path's length</param> private void SetupPath(int index, int last, int length) { if (index >= paths.Length) { Array.Resize(ref paths, paths.Length + INIT_PATHS_COUNT); } if (paths[index] == null) { paths[index] = new GSSPath(length); } else { paths[index].Ensure(length); } paths[index].Last = last; paths[index].Generation = GetGenerationOf(last); }
/// <summary> /// Builds the SPPF /// </summary> /// <param name="generation">The current GSS generation</param> /// <param name="production">The LR production</param> /// <param name="first">The first label of the path</param> /// <param name="path">The reduction path</param> /// <returns>The identifier of the corresponding SPPF node</returns> private int BuildSPPF(int generation, LRProduction production, int first, GSSPath path) { Symbol variable = symVariables[production.Head]; sppf.ReductionPrepare(first, path, production.ReductionLength); for (int i = 0; i != production.BytecodeLength; i++) { LROpCode op = production[i]; switch (op.Base) { case LROpCodeBase.SemanticAction: { SemanticAction action = symActions[production[i + 1].DataValue]; i++; action.Invoke(variable, sppf); break; } case LROpCodeBase.AddVirtual: { int index = production[i + 1].DataValue; sppf.ReductionAddVirtual(index, op.TreeAction); i++; break; } case LROpCodeBase.AddNullVariable: { int index = production[i + 1].DataValue; sppf.ReductionAddNullable(nullables[index], op.TreeAction); i++; break; } default: sppf.ReductionPop(op.TreeAction); break; } } return(sppf.Reduce(generation, production.Head, production.HeadAction)); }
/// <summary> /// Copy the content of another path to this one /// </summary> /// <param name="path">The path to copy</param> /// <param name="length">The path's length</param> public void CopyLabelsFrom(GSSPath path, int length) { Array.Copy(path.labels, labels, length); }
/// <summary> /// Gets all paths in the GSS starting at the given node and with the given length /// </summary> /// <param name="from">The starting node</param> /// <param name="length">The length of the requested paths</param> /// <param name="count">The number of paths</param> /// <returns>A collection of paths in this GSS</returns> public GSSPath[] GetPaths(int from, int length, out int count) { if (length == 0) { // use the common 0-length GSS path to avoid new memory allocation path0.Last = from; count = 1; return(paths0); } // Initializes the first path SetupPath(0, from, length); // The number of paths in the list int total = 1; // For the remaining hops for (int i = 0; i != length; i++) { int m = 0; // Insertion index for the compaction process int next = total; // Insertion index for new paths for (int p = 0; p != total; p++) { int last = paths[p].Last; int genIndex = paths[p].Generation; // Look for new additional paths from last GSSGeneration gen = edgeGenerations[genIndex]; int firstEdgeTarget = -1; int firstEdgeLabel = -1; for (int e = gen.Start; e != gen.Start + gen.Count; e++) { GSSEdge edge = edges[e]; if (edge.From == last) { if (firstEdgeTarget == -1) { // This is the first edge firstEdgeTarget = edge.To; firstEdgeLabel = edge.Label; } else { // Not the first edge // Clone and extend the new path SetupPath(next, edge.To, length); paths[next].CopyLabelsFrom(paths[p], i); paths[next][i] = edge.Label; // Go to next insert next++; } } } // Check whether there was at least one edge if (firstEdgeTarget != -1) { // Continue the current path if (m != p) { GSSPath t = paths[m]; paths[m] = paths[p]; paths[p] = t; } paths[m].Last = firstEdgeTarget; paths[m].Generation = GetGenerationOf(firstEdgeTarget); paths[m][i] = firstEdgeLabel; // goto next m++; } } if (m != total) { // if some previous paths have been removed // => compact the list if needed for (int p = total; p != next; p++) { GSSPath t = paths[m]; paths[m] = paths[p]; paths[p] = t; m++; } // m is now the exact number of paths total = m; } else if (next != total) { // no path has been removed, but some have been added // => next is the exact number of paths total = next; } } count = total; return(paths); }
/// <summary> /// Executes a reduction operation for a given path /// </summary> /// <param name="generation">The current GSS generation</param> /// <param name="reduction">The reduction operation</param> /// <param name="path">The GSS path to use for the reduction</param> private void ExecuteReduction(int generation, Reduction reduction, GSSPath path) { // Get the rule's head Symbol head = symVariables[reduction.prod.Head]; // Resolve the sub-root int label = sppf.GetLabelFor(path.Generation, new TableElemRef(TableType.Variable, reduction.prod.Head)); if (label == SPPF.EPSILON) { // not in history, build the SPPF here label = BuildSPPF(generation, reduction.prod, reduction.first, path); } // Get the target state by transition on the rule's head int to = GetNextByVar(gss.GetRepresentedState(path.Last), head.ID); // Find a node for the target state in the GSS int w = gss.FindNode(generation, to); if (w != -1) { // A node for the target state is already in the GSS if (!gss.HasEdge(generation, w, path.Last)) { // But the new edge does not exist gss.CreateEdge(w, path.Last, label); // Look for the new reductions at this state if (reduction.prod.ReductionLength != 0) { int count = parserAutomaton.GetActionsCount(to, nextToken.TerminalID); for (int i = 0; i != count; i++) { LRAction action = parserAutomaton.GetAction(to, nextToken.TerminalID, i); if (action.Code == LRActionCode.Reduce) { LRProduction prod = parserAutomaton.GetProduction(action.Data); // length 0 reduction are not considered here because they already exist at this point if (prod.ReductionLength != 0) { reductions.Enqueue(new Reduction(path.Last, prod, label)); } } } } } } else { // Create the new corresponding node in the GSS w = gss.CreateNode(to); gss.CreateEdge(w, path.Last, label); // Look for all the reductions and shifts at this state int count = parserAutomaton.GetActionsCount(to, nextToken.TerminalID); for (int i = 0; i != count; i++) { LRAction action = parserAutomaton.GetAction(to, nextToken.TerminalID, i); if (action.Code == LRActionCode.Shift) { shifts.Enqueue(new Shift(w, action.Data)); } else if (action.Code == LRActionCode.Reduce) { LRProduction prod = parserAutomaton.GetProduction(action.Data); if (prod.ReductionLength == 0) { reductions.Enqueue(new Reduction(w, prod, SPPF.EPSILON)); } else if (reduction.prod.ReductionLength != 0) { reductions.Enqueue(new Reduction(path.Last, prod, label)); } } } } }
/// <summary> /// Checks whether the specified terminal is indeed expected for a reduction /// </summary> /// <param name="gssNode">The GSS node from which to reduce</param> /// <param name="terminal">The terminal to check</param> /// <returns><code>true</code> if the terminal is really expected</returns> /// <remarks> /// This check is required because in the case of a base LALR graph, /// some terminals expected for reduction in the automaton are coming from other paths. /// </remarks> private bool CheckIsExpected(int gssNode, Symbol terminal) { // queue of GLR states to inspect: List <int> queueGSSHead = new List <int>(); // the related GSS head List <int[]> queueVStack = new List <int[]>(); // the virtual stack // first reduction { int count = parserAutomaton.GetActionsCount(gss.GetRepresentedState(gssNode), terminal.ID); for (int j = 0; j != count; j++) { LRAction action = parserAutomaton.GetAction(gss.GetRepresentedState(gssNode), terminal.ID, j); if (action.Code == LRActionCode.Reduce) { // execute the reduction LRProduction production = parserAutomaton.GetProduction(action.Data); int nbPaths; GSSPath[] paths = gss.GetPaths(gssNode, production.ReductionLength, out nbPaths); for (int k = 0; k != nbPaths; k++) { GSSPath path = paths[k]; // get the target GLR state int next = GetNextByVar(gss.GetRepresentedState(path.Last), symVariables[production.Head].ID); // enqueue the info, top GSS stack node and target GLR state queueGSSHead.Add(path.Last); queueVStack.Add(new[] { next }); } } } } // now, close the queue for (int i = 0; i != queueGSSHead.Count; i++) { int head = queueVStack[i][queueVStack[i].Length - 1]; int count = parserAutomaton.GetActionsCount(head, terminal.ID); if (count == 0) { continue; } for (int j = 0; j != count; j++) { LRAction action = parserAutomaton.GetAction(head, terminal.ID, j); if (action.Code == LRActionCode.Shift) { // yep, the terminal was expected return(true); } if (action.Code == LRActionCode.Reduce) { // execute the reduction LRProduction production = parserAutomaton.GetProduction(action.Data); if (production.ReductionLength == 0) { // 0-length reduction => start from the current head int[] virtualStack = new int[queueVStack[i].Length + 1]; Array.Copy(queueVStack[i], virtualStack, queueVStack[i].Length); virtualStack[virtualStack.Length - 1] = GetNextByVar(head, symVariables[production.Head].ID); // enqueue queueGSSHead.Add(queueGSSHead[i]); queueVStack.Add(virtualStack); } else if (production.ReductionLength < queueVStack[i].Length) { // we are still the virtual stack int[] virtualStack = new int[queueVStack[i].Length - production.ReductionLength + 1]; Array.Copy(queueVStack[i], virtualStack, virtualStack.Length - 1); virtualStack[virtualStack.Length - 1] = GetNextByVar(virtualStack[virtualStack.Length - 2], symVariables[production.Head].ID); // enqueue queueGSSHead.Add(queueGSSHead[i]); queueVStack.Add(virtualStack); } else { // we reach the GSS int nbPaths; GSSPath[] paths = gss.GetPaths(queueGSSHead[i], production.ReductionLength - queueVStack[i].Length, out nbPaths); for (int k = 0; k != nbPaths; k++) { GSSPath path = paths[k]; // get the target GLR state int next = GetNextByVar(gss.GetRepresentedState(path.Last), symVariables[production.Head].ID); // enqueue the info, top GSS stack node and target GLR state queueGSSHead.Add(path.Last); queueVStack.Add(new[] { next }); } } } } } // nope, that was a pathological case in a LALR graph return(false); }
/// <summary> /// Gets the priority of the specified context required by the specified terminal /// The priority is a positive integer. The lesser the value the higher the priority. /// A value of -1 represents the unavailability of the required context. /// </summary> /// <param name="context">A context</param> /// <param name="onTerminalID">The identifier of the terminal requiring the context</param> /// <returns>The context priority, or -1 if the context is unavailable</returns> public int GetContextPriority(int context, int onTerminalID) { // the default context is always active if (context == Automaton.DEFAULT_CONTEXT) { return(int.MaxValue); } if (lexer.tokens.Size == 0) { // this is the first token, does it open the context? return(parserAutomaton.GetContexts(0).Opens(onTerminalID, context) ? 0 : -1); } // try to only look at stack heads that expect the terminal List <int> queue = new List <int>(); List <LRProduction> productions = new List <LRProduction>(); List <int> distances = new List <int>(); bool foundOnPreviousShift = false; foreach (Shift shift in shifts) { int count = parserAutomaton.GetActionsCount(shift.to, onTerminalID); if (count == 0) { continue; } for (int i = 0; i != count; i++) { LRAction action = parserAutomaton.GetAction(shift.to, onTerminalID, i); if (action.Code == LRActionCode.Shift) { // does the context opens with the terminal? if (parserAutomaton.GetContexts(shift.to).Opens(onTerminalID, context)) { return(0); } // looking at the immediate history, does the context opens from the shift just before? if (parserAutomaton.GetContexts(gss.GetRepresentedState(shift.from)).Opens(nextToken.TerminalID, context)) { foundOnPreviousShift = true; break; } // no, enqueue if (!queue.Contains(shift.from)) { queue.Add(shift.from); productions.Add(null); distances.Add(2); } } else { // this is reduction LRProduction production = parserAutomaton.GetProduction(action.Data); // looking at the immediate history, does the context opens from the shift just before? if (parserAutomaton.GetContexts(gss.GetRepresentedState(shift.from)).Opens(nextToken.TerminalID, context)) { if (production.ReductionLength < 1) { // the reduction does not close the context foundOnPreviousShift = true; break; } } // no, enqueue if (!queue.Contains(shift.from)) { queue.Add(shift.from); productions.Add(production); distances.Add(2); } } } } if (foundOnPreviousShift) { // found the context opening on the previous shift (and was not immediately closed by a reduction) return(1); } if (queue.Count == 0) { // the track is empty, the terminal is unexpected return(-1); } // explore the current GSS to find the specified context for (int i = 0; i != queue.Count; i++) { int count; GSSPath[] paths = gss.GetPaths(queue[i], 1, out count); for (int p = 0; p != count; p++) { int from = paths[p].Last; int symbolID = sppf.GetSymbolOn(paths[p][0]).ID; int distance = distances[i]; LRProduction production = productions[i]; // was the context opened on this transition? if (parserAutomaton.GetContexts(gss.GetRepresentedState(from)).Opens(symbolID, context)) { if (production == null || production.ReductionLength < distance) { return(distance); } } // no, enqueue if (!queue.Contains(from)) { queue.Add(from); productions.Add(production); distances.Add(distance + 1); } } } // at this point, the requested context is not yet open // can it be open by a token with the specified terminal ID? // queue of GLR states to inspect: List <int> queueGSSHead = new List <int>(); // the related GSS head List <int[]> queueVStack = new List <int[]>(); // the virtual stack // first reduction foreach (Shift shift in shifts) { int count = parserAutomaton.GetActionsCount(shift.to, onTerminalID); if (count > 0) { // enqueue the info, top GSS stack node and target GLR state queueGSSHead.Add(shift.from); queueVStack.Add(new[] { shift.to }); } } // now, close the queue for (int i = 0; i != queueGSSHead.Count; i++) { int head = queueVStack[i][queueVStack[i].Length - 1]; int count = parserAutomaton.GetActionsCount(head, onTerminalID); if (count == 0) { continue; } for (int j = 0; j != count; j++) { LRAction action = parserAutomaton.GetAction(head, onTerminalID, j); if (action.Code != LRActionCode.Reduce) { continue; } // execute the reduction LRProduction production = parserAutomaton.GetProduction(action.Data); if (production.ReductionLength == 0) { // 0-length reduction => start from the current head int[] virtualStack = new int[queueVStack[i].Length + 1]; Array.Copy(queueVStack[i], virtualStack, queueVStack[i].Length); int next = GetNextByVar(head, symVariables[production.Head].ID); virtualStack[virtualStack.Length - 1] = next; // enqueue queueGSSHead.Add(queueGSSHead[i]); queueVStack.Add(virtualStack); } else if (production.ReductionLength < queueVStack[i].Length) { // we are still the virtual stack int[] virtualStack = new int[queueVStack[i].Length - production.ReductionLength + 1]; Array.Copy(queueVStack[i], virtualStack, virtualStack.Length - 1); int next = GetNextByVar(virtualStack[virtualStack.Length - 2], symVariables[production.Head].ID); virtualStack[virtualStack.Length - 1] = next; // enqueue queueGSSHead.Add(queueGSSHead[i]); queueVStack.Add(virtualStack); } else { // we reach the GSS int nbPaths; GSSPath[] paths = gss.GetPaths(queueGSSHead[i], production.ReductionLength - queueVStack[i].Length, out nbPaths); for (int k = 0; k != nbPaths; k++) { GSSPath path = paths[k]; // get the target GLR state int next = GetNextByVar(gss.GetRepresentedState(path.Last), symVariables[production.Head].ID); // enqueue the info, top GSS stack node and target GLR state queueGSSHead.Add(path.Last); queueVStack.Add(new[] { next }); } } } } foreach (int[] vstack in queueVStack) { int state = vstack[vstack.Length - 1]; int count = parserAutomaton.GetActionsCount(state, onTerminalID); for (int i = 0; i != count; i++) { LRAction action = parserAutomaton.GetAction(state, onTerminalID, i); if (action.Code == LRActionCode.Shift && parserAutomaton.GetContexts(state).Opens(onTerminalID, context)) { // the context opens here return(0); } } } // the context is still unavailable return(-1); }