/*******************************************************************************
* Copyright (c) 2009-2013 CWI
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* * Arnold Lankamp - Arnold.Lankamp@cwi.nl
*******************************************************************************/
package org.rascalmpl.parser.gtd;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.URI;
import org.rascalmpl.parser.gtd.debug.IDebugListener;
import org.rascalmpl.parser.gtd.exception.ParseError;
import org.rascalmpl.parser.gtd.exception.UndeclaredNonTerminalException;
import org.rascalmpl.parser.gtd.location.PositionStore;
import org.rascalmpl.parser.gtd.recovery.IRecoverer;
import org.rascalmpl.parser.gtd.result.AbstractContainerNode;
import org.rascalmpl.parser.gtd.result.AbstractNode;
import org.rascalmpl.parser.gtd.result.ExpandableContainerNode;
import org.rascalmpl.parser.gtd.result.RecoveredNode;
import org.rascalmpl.parser.gtd.result.SortContainerNode;
import org.rascalmpl.parser.gtd.result.action.IActionExecutor;
import org.rascalmpl.parser.gtd.result.action.VoidActionExecutor;
import org.rascalmpl.parser.gtd.result.out.FilteringTracker;
import org.rascalmpl.parser.gtd.result.out.INodeConstructorFactory;
import org.rascalmpl.parser.gtd.result.out.INodeFlattener;
import org.rascalmpl.parser.gtd.result.struct.Link;
import org.rascalmpl.parser.gtd.stack.AbstractExpandableStackNode;
import org.rascalmpl.parser.gtd.stack.AbstractStackNode;
import org.rascalmpl.parser.gtd.stack.EpsilonStackNode;
import org.rascalmpl.parser.gtd.stack.NonTerminalStackNode;
import org.rascalmpl.parser.gtd.stack.edge.EdgesSet;
import org.rascalmpl.parser.gtd.stack.filter.ICompletionFilter;
import org.rascalmpl.parser.gtd.stack.filter.IEnterFilter;
import org.rascalmpl.parser.gtd.util.ArrayList;
import org.rascalmpl.parser.gtd.util.DoubleArrayList;
import org.rascalmpl.parser.gtd.util.DoubleStack;
import org.rascalmpl.parser.gtd.util.HashMap;
import org.rascalmpl.parser.gtd.util.IntegerKeyedDoubleValueHashMap;
import org.rascalmpl.parser.gtd.util.IntegerList;
import org.rascalmpl.parser.gtd.util.IntegerObjectList;
import org.rascalmpl.parser.gtd.util.Stack;
/**
* This is the core of the parser; it drives the parse process.
*/
public abstract class SGTDBF<P, T, S> implements IGTD<P, T, S>{
private final static int DEFAULT_TODOLIST_CAPACITY = 16;
private URI inputURI;
private int[] input;
private final PositionStore positionStore;
private DoubleStack<AbstractStackNode<P>, AbstractNode>[] todoLists;
private int queueIndex;
private final Stack<AbstractStackNode<P>> stacksToExpand;
private final DoubleStack<AbstractStackNode<P>, AbstractContainerNode<P>> stacksWithNonTerminalsToReduce;
private DoubleStack<AbstractStackNode<P>, AbstractNode> stacksWithTerminalsToReduce;
private final HashMap<String, EdgesSet<P>> cachedEdgesForExpect;
private final IntegerKeyedDoubleValueHashMap<AbstractStackNode<P>, DoubleArrayList<AbstractStackNode<P>, AbstractNode>> sharedNextNodes;
private int location;
protected int lookAheadChar;
private final HashMap<String, AbstractStackNode<P>[]> expectCache;
private final IntegerObjectList<AbstractStackNode<P>> sharedLastExpects;
// Guard
private boolean invoked;
// Error reporting
private final Stack<AbstractStackNode<P>> unexpandableNodes;
private final Stack<AbstractStackNode<P>> unmatchableLeafNodes;
private final DoubleStack<DoubleArrayList<AbstractStackNode<P>, AbstractNode>, AbstractStackNode<P>> unmatchableMidProductionNodes;
private final DoubleStack<AbstractStackNode<P>, AbstractNode> filteredNodes;
// Error reporting guards
private boolean parseErrorOccured;
// Error recovery
private IRecoverer<P> recoverer;
// Debugging
private IDebugListener<P> debugListener;
// Temporary instrumentation for accurate profiling
private long timestamp;
private boolean printTimes = false;
public SGTDBF(){
super();
positionStore = new PositionStore();
stacksToExpand = new Stack<AbstractStackNode<P>>();
stacksWithNonTerminalsToReduce = new DoubleStack<AbstractStackNode<P>, AbstractContainerNode<P>>();
cachedEdgesForExpect = new HashMap<String, EdgesSet<P>>();
sharedNextNodes = new IntegerKeyedDoubleValueHashMap<AbstractStackNode<P>, DoubleArrayList<AbstractStackNode<P>, AbstractNode>>();
location = 0;
expectCache = new HashMap<String, AbstractStackNode<P>[]>();
sharedLastExpects = new IntegerObjectList<AbstractStackNode<P>>();
unexpandableNodes = new Stack<AbstractStackNode<P>>();
unmatchableLeafNodes = new Stack<AbstractStackNode<P>>();
unmatchableMidProductionNodes = new DoubleStack<DoubleArrayList<AbstractStackNode<P>, AbstractNode>, AbstractStackNode<P>>();
filteredNodes = new DoubleStack<AbstractStackNode<P>, AbstractNode>();
}
/**
* Triggers the gathering of alternatives for the given non-terminal.
*/
@SuppressWarnings("unchecked")
protected AbstractStackNode<P>[] invokeExpects(AbstractStackNode<P> nonTerminal){
String name = nonTerminal.getName();
AbstractStackNode<P>[] expects = expectCache.get(name);
if(expects == null){
try{
Method method = getClass().getMethod(name);
try{
method.setAccessible(true); // Try to bypass the 'isAccessible' check to save time.
}catch(SecurityException sex){
// Ignore this if it happens.
}
expects = (AbstractStackNode<P>[]) method.invoke(this);
}catch(NoSuchMethodException nsmex){
throw new UndeclaredNonTerminalException(name, getClass());
}catch(IllegalAccessException iaex){
throw new RuntimeException(iaex);
}catch(InvocationTargetException itex){
throw new RuntimeException(itex.getTargetException());
}
expectCache.putUnsafe(name, expects);
}
return expects;
}
/**
* Moves to the next symbol in the production.
*/
private AbstractStackNode<P> updateNextNode(AbstractStackNode<P> next, AbstractStackNode<P> node, AbstractNode result){
IntegerKeyedDoubleValueHashMap.Entry<AbstractStackNode<P>, DoubleArrayList<AbstractStackNode<P>, AbstractNode>> alternativeEntry = sharedNextNodes.get(next.getId());
if(alternativeEntry != null){ // Sharing check.
DoubleArrayList<AbstractStackNode<P>, AbstractNode> predecessors = alternativeEntry.value2;
if(predecessors != null){
predecessors.add(node, result);
return null;
}
AbstractStackNode<P> alternative = alternativeEntry.value1;
if(result.isEmpty()){
if(alternative.isMatchable()){
if(alternative.isEmptyLeafNode()){
// Encountered a possible stack 'overtake'.
if(node.getStartLocation() != location){
propagateEdgesAndPrefixes(node, result, alternative, alternative.getResult());
}else{
propagateEdgesAndPrefixesForNullable(node, result, alternative, alternative.getResult(), node.getEdges().size());
}
return alternative;
}
}else{
if(alternative.getStartLocation() == location){
EdgesSet<P> alternativeEdgesSet = alternative.getIncomingEdges();
int resultStoreId = getResultStoreId(alternative.getId());
if(alternativeEdgesSet != null && alternativeEdgesSet.getLastVisitedLevel(resultStoreId) == location){
// Encountered a possible stack 'overtake'.
if(node.getStartLocation() != location){
propagateEdgesAndPrefixes(node, result, alternative, alternativeEdgesSet.getLastResult(resultStoreId));
}else{
propagateEdgesAndPrefixesForNullable(node, result, alternative, alternativeEdgesSet.getLastResult(resultStoreId), node.getEdges().size());
}
return alternative;
}
}
}
}
alternative.updateNode(node, result);
if(debugListener != null) debugListener.progressed(node, result, alternative);
return alternative;
}
if(next.isMatchable()){ // Eager matching optimization.
if((location + next.getLength()) > input.length) return null;
AbstractNode nextResult = next.match(input, location);
if(nextResult == null){
// Push the node including it's predecessor to the appropriate error tracking collection (and take care of merging when necessary).
DoubleArrayList<AbstractStackNode<P>, AbstractNode> predecessors = new DoubleArrayList<AbstractStackNode<P>, AbstractNode>();
predecessors.add(node, result);
unmatchableMidProductionNodes.push(predecessors, next);
sharedNextNodes.putUnsafe(next.getId(), next, predecessors);
if(debugListener != null) debugListener.failedToMatch(next);
return null;
}
if(debugListener != null) debugListener.matched(next, nextResult);
next = next.getCleanCopyWithResult(location, nextResult);
}else{
next = next.getCleanCopy(location);
}
if(!node.isMatchable() || result.isEmpty()){
next.updateNode(node, result);
}else{ // Non-nullable terminal specific edge set sharing optimization.
next.updateNodeAfterNonEmptyMatchable(node, result);
}
if(debugListener != null) debugListener.progressed(node, result, next);
sharedNextNodes.putUnsafe(next.getId(), next, null);
stacksToExpand.push(next);
return next;
}
/**
* Moves to the next symbol in an alternative continuation of a prefix-shared production.
*/
private boolean updateAlternativeNextNode(AbstractStackNode<P> next, AbstractStackNode<P> node, AbstractNode result, IntegerObjectList<EdgesSet<P>> edgesMap, ArrayList<Link>[] prefixesMap){
int id = next.getId();
IntegerKeyedDoubleValueHashMap.Entry<AbstractStackNode<P>, DoubleArrayList<AbstractStackNode<P>, AbstractNode>> alternativeEntry = sharedNextNodes.get(id);
if(alternativeEntry != null){ // Sharing check.
DoubleArrayList<AbstractStackNode<P>, AbstractNode> predecessors = alternativeEntry.value2;
if(predecessors != null){
predecessors.add(node, result);
return false;
}
AbstractStackNode<P> alternative = alternativeEntry.value1;
if(result.isEmpty()){
if(alternative.isMatchable()){
if(alternative.isEmptyLeafNode()){
// Encountered a possible stack 'overtake'.
propagateAlternativeEdgesAndPrefixes(node, result, alternative, alternative.getResult(), node.getEdges().size(), edgesMap, prefixesMap);
return true;
}
}else{
EdgesSet<P> alternativeEdgesSet = alternative.getIncomingEdges();
int resultStoreId = getResultStoreId(alternative.getId());
if(alternativeEdgesSet != null && alternativeEdgesSet.getLastVisitedLevel(resultStoreId) == location){
AbstractContainerNode<P> nextResult = alternativeEdgesSet.getLastResult(resultStoreId);
// Encountered a possible stack 'overtake'.
propagateAlternativeEdgesAndPrefixes(node, result, alternative, nextResult, node.getEdges().size(), edgesMap, prefixesMap);
return true;
}
}
}
alternative.updatePrefixSharedNode(edgesMap, prefixesMap); // Prevent unnecessary overhead; share whenever possible.
if(debugListener != null) debugListener.progressed(node, result, alternative);
return true;
}
if(next.isMatchable()){ // Eager matching optimization.
if((location + next.getLength()) > input.length) return false;
AbstractNode nextResult = next.match(input, location);
if(nextResult == null){
// Push the node including it's predecessor to the appropriate error tracking collection (and take care of merging when necessary).
DoubleArrayList<AbstractStackNode<P>, AbstractNode> predecessors = new DoubleArrayList<AbstractStackNode<P>, AbstractNode>();
predecessors.add(node, result);
unmatchableMidProductionNodes.push(predecessors, next);
sharedNextNodes.putUnsafe(id, next, predecessors);
if(debugListener != null) debugListener.failedToMatch(next);
return false;
}
if(debugListener != null) debugListener.matched(next, nextResult);
next = next.getCleanCopyWithResult(location, nextResult);
}else{
next = next.getCleanCopy(location);
}
next.updatePrefixSharedNode(edgesMap, prefixesMap); // Prevent unnecessary overhead; share whenever possible.
if(debugListener != null) debugListener.progressed(node, result, next);
sharedNextNodes.putUnsafe(id, next, null);
stacksToExpand.push(next);
return true;
}
/**
* Part of the hidden-right-recursion fix.
* Executes absent reductions.
*/
private void propagateReductions(AbstractStackNode<P> node, AbstractNode nodeResultStore, AbstractStackNode<P> next, AbstractNode nextResultStore, int potentialNewEdges){
IntegerList propagatedReductions = next.getPropagatedReductions();
IntegerObjectList<EdgesSet<P>> edgesMap = node.getEdges();
ArrayList<Link>[] prefixes = node.getPrefixesMap();
P production = next.getParentProduction();
String name = edgesMap.getValue(0).get(0).getName();
boolean hasNestingRestrictions = hasNestingRestrictions(name);
IntegerList filteredParents = null;
if(hasNestingRestrictions){
filteredParents = getFilteredParents(next.getId());
}
int fromIndex = edgesMap.size() - potentialNewEdges;
for(int i = edgesMap.size() - 1; i >= fromIndex; --i){
int startLocation = edgesMap.getKey(i);
// We know we haven't been here before.
propagatedReductions.add(startLocation);
ArrayList<Link> edgePrefixes = new ArrayList<Link>();
Link prefix = (prefixes != null) ? new Link(prefixes[i], nodeResultStore) : new Link(null, nodeResultStore);
edgePrefixes.add(prefix);
Link resultLink = new Link(edgePrefixes, nextResultStore);
EdgesSet<P> edgeSet = edgesMap.getValue(i);
if(debugListener != null) debugListener.reducing(node, resultLink, edgeSet);
if(!hasNestingRestrictions){
handleEdgeList(edgeSet, name, production, resultLink, startLocation);
}else{
handleEdgeListWithRestrictions(edgeSet, name, production, resultLink, startLocation, filteredParents);
}
}
}
/**
* Part of the hidden-right-recursion fix.
* Propagates absent prefixes.
*/
private void propagatePrefixes(AbstractStackNode<P> next, AbstractNode nextResult, int nrOfAddedEdges){
// Proceed with the tail of the production.
int nextDot = next.getDot() + 1;
AbstractStackNode<P>[] prod = next.getProduction();
AbstractStackNode<P> nextNext = prod[nextDot];
IntegerKeyedDoubleValueHashMap.Entry<AbstractStackNode<P>, DoubleArrayList<AbstractStackNode<P>, AbstractNode>> nextNextAlternativeEntry = sharedNextNodes.get(nextNext.getId());
AbstractStackNode<P> nextNextAlternative = null;
if(nextNextAlternativeEntry != null){ // Valid continuation.
DoubleArrayList<AbstractStackNode<P>, AbstractNode> predecessors = nextNextAlternativeEntry.value2;
if(predecessors == null){
nextNextAlternative = nextNextAlternativeEntry.value1;
if(nextNextAlternative.isMatchable()){
if(nextNextAlternative.isEmptyLeafNode()){
propagateEdgesAndPrefixesForNullable(next, nextResult, nextNextAlternative, nextNextAlternative.getResult(), nrOfAddedEdges);
}else{
nextNextAlternative.updateNode(next, nextResult);
if(debugListener != null) debugListener.propagated(next, nextResult, nextNextAlternative);
}
}else{
EdgesSet<P> nextNextAlternativeEdgesSet = nextNextAlternative.getIncomingEdges();
int resultStoreId = getResultStoreId(nextNextAlternative.getId());
if(nextNextAlternativeEdgesSet != null && nextNextAlternativeEdgesSet.getLastVisitedLevel(resultStoreId) == location){
propagateEdgesAndPrefixesForNullable(next, nextResult, nextNextAlternative, nextNextAlternativeEdgesSet.getLastResult(resultStoreId), nrOfAddedEdges);
}else{
nextNextAlternative.updateNode(next, nextResult);
if(debugListener != null) debugListener.propagated(next, nextResult, nextNextAlternative);
}
}
}else{
predecessors.add(next, nextResult);
}
}
// Handle alternative continuations (related to prefix sharing).
AbstractStackNode<P>[][] alternateProds = next.getAlternateProductions();
if(alternateProds != null){
if(nextNextAlternative == null){ // If the first continuation has not been initialized yet (it may be a matchable that didn't match), create a dummy version to construct the necessary edges and prefixes.
if(!nextNext.isMatchable()) return; // Matchable, abort.
nextNextAlternative = nextNext.getCleanCopy(location);
nextNextAlternative.updateNode(next, nextResult);
if(debugListener != null) debugListener.propagated(next, nextResult, nextNextAlternative);
}
IntegerObjectList<EdgesSet<P>> nextNextEdgesMap = nextNextAlternative.getEdges();
ArrayList<Link>[] nextNextPrefixesMap = nextNextAlternative.getPrefixesMap();
for(int i = alternateProds.length - 1; i >= 0; --i){
prod = alternateProds[i];
if(nextDot == prod.length) continue;
AbstractStackNode<P> alternativeNextNext = prod[nextDot];
IntegerKeyedDoubleValueHashMap.Entry<AbstractStackNode<P>, DoubleArrayList<AbstractStackNode<P>, AbstractNode>> nextNextAltAlternativeEntry = sharedNextNodes.get(alternativeNextNext.getId());
AbstractStackNode<P> nextNextAltAlternative = null;
if(nextNextAltAlternativeEntry != null){
DoubleArrayList<AbstractStackNode<P>, AbstractNode> predecessors = nextNextAltAlternativeEntry.value2;
if(predecessors == null){
nextNextAltAlternative = nextNextAltAlternativeEntry.value1;
if(nextNextAltAlternative.isMatchable()){
if(nextNextAltAlternative.isEmptyLeafNode()){
propagateAlternativeEdgesAndPrefixes(next, nextResult, nextNextAltAlternative, nextNextAltAlternative.getResult(), nrOfAddedEdges, nextNextEdgesMap, nextNextPrefixesMap);
}else{
nextNextAltAlternative.updatePrefixSharedNode(nextNextEdgesMap, nextNextPrefixesMap);
if(debugListener != null) debugListener.propagated(next, nextResult, nextNextAltAlternative);
}
}else{
EdgesSet<P> nextAlternativeEdgesSet = nextNextAlternative.getIncomingEdges();
int resultStoreId = getResultStoreId(nextNextAltAlternative.getId());
if(nextAlternativeEdgesSet != null && nextAlternativeEdgesSet.getLastVisitedLevel(resultStoreId) == location){
propagateAlternativeEdgesAndPrefixes(next, nextResult, nextNextAltAlternative, nextAlternativeEdgesSet.getLastResult(resultStoreId), nrOfAddedEdges, nextNextEdgesMap, nextNextPrefixesMap);
}else{
nextNextAltAlternative.updatePrefixSharedNode(nextNextEdgesMap, nextNextPrefixesMap);
if(debugListener != null) debugListener.propagated(next, nextResult, nextNextAltAlternative);
}
}
}else{
predecessors.add(next, nextResult);
}
}
}
}
}
/**
* Part of the hidden-right-recursion fix.
* Inserts missing prefixes and triggers reductions where necessary.
*/
private void propagateEdgesAndPrefixes(AbstractStackNode<P> node, AbstractNode nodeResult, AbstractStackNode<P> next, AbstractNode nextResult){
int nrOfAddedEdges = next.updateOvertakenNode(node, nodeResult);
if(debugListener != null) debugListener.propagated(node, nodeResult, next);
if(nrOfAddedEdges == 0) return;
if(next.isEndNode()){
propagateReductions(node, nodeResult, next, nextResult, nrOfAddedEdges);
}
if(next.hasNext()){
propagatePrefixes(next, nextResult, nrOfAddedEdges);
}
}
/**
* Part of the hidden-right-recursion fix.
* Inserts missing prefixes and triggers reductions where necessary (specific for nullable nodes).
*/
private void propagateEdgesAndPrefixesForNullable(AbstractStackNode<P> node, AbstractNode nodeResult, AbstractStackNode<P> next, AbstractNode nextResult, int potentialNewEdges){
int nrOfAddedEdges = next.updateOvertakenNullableNode(node, nodeResult, potentialNewEdges);
if(debugListener != null) debugListener.propagated(node, nodeResult, next);
if(nrOfAddedEdges == 0) return;
if(next.isEndNode()){
propagateReductions(node, nodeResult, next, nextResult, nrOfAddedEdges);
}
if(next.hasNext()){
propagatePrefixes(next, nextResult, nrOfAddedEdges);
}
}
/**
* Part of the hidden-right-recursion fix.
* Inserts missing prefixes and triggers reductions where necessary (specifically for alternative continuations of prefix-shared productions).
*/
private void propagateAlternativeEdgesAndPrefixes(AbstractStackNode<P> node, AbstractNode nodeResult, AbstractStackNode<P> next, AbstractNode nextResult, int potentialNewEdges, IntegerObjectList<EdgesSet<P>> edgesMap, ArrayList<Link>[] prefixesMap){
next.updatePrefixSharedNode(edgesMap, prefixesMap);
if(debugListener != null) debugListener.propagated(node, nodeResult, next);
if(potentialNewEdges == 0) return;
if(next.isEndNode()){
propagateReductions(node, nodeResult, next, nextResult, potentialNewEdges);
}
if(next.hasNext()){
propagatePrefixes(next, nextResult, potentialNewEdges);
}
}
/**
* Initiates the handling of reductions.
*/
private void updateEdges(AbstractStackNode<P> node, AbstractNode result){
IntegerObjectList<EdgesSet<P>> edgesMap = node.getEdges();
ArrayList<Link>[] prefixesMap = node.getPrefixesMap();
P production = node.getParentProduction();
String name = edgesMap.getValue(0).get(0).getName();
// Check for nesting restrictions.
boolean hasNestingRestrictions = hasNestingRestrictions(name);
IntegerList filteredParents = null;
if(hasNestingRestrictions){
filteredParents = getFilteredParents(node.getId());
}
for(int i = edgesMap.size() - 1; i >= 0; --i){
Link resultLink = new Link((prefixesMap != null) ? prefixesMap[i] : null, result);
EdgesSet<P> edgeSet = edgesMap.getValue(i);
if(debugListener != null) debugListener.reducing(node, resultLink, edgeSet);
if(!hasNestingRestrictions){ // Select the optimized path for handling edge sets that don't have nesting restrictions associated with them.
handleEdgeList(edgeSet, name, production, resultLink, edgesMap.getKey(i));
}else{
handleEdgeListWithRestrictions(edgeSet, name, production, resultLink, edgesMap.getKey(i), filteredParents);
}
}
}
/**
* Initiates the handling of reductions for nullable symbols.
*/
private void updateNullableEdges(AbstractStackNode<P> node, AbstractNode result){
IntegerList propagatedReductions = node.getPropagatedReductions();
int initialSize = propagatedReductions.size();
IntegerObjectList<EdgesSet<P>> edgesMap = node.getEdges();
ArrayList<Link>[] prefixesMap = node.getPrefixesMap();
P production = node.getParentProduction();
String name = edgesMap.getValue(0).get(0).getName();
// Check for nesting restrictions.
boolean hasNestingRestrictions = hasNestingRestrictions(name);
IntegerList filteredParents = null;
if(hasNestingRestrictions){
filteredParents = getFilteredParents(node.getId());
}
for(int i = edgesMap.size() - 1; i >= 0; --i){
int startLocation = edgesMap.getKey(i);
if(propagatedReductions.containsBefore(startLocation, initialSize)) continue; // Prevent duplicate reductions (artifact of the hidden-right-recursion fix).
propagatedReductions.add(startLocation);
Link resultLink = new Link((prefixesMap != null) ? prefixesMap[i] : null, result);
EdgesSet<P> edgeSet = edgesMap.getValue(i);
if(debugListener != null) debugListener.reducing(node, resultLink, edgeSet);
if(!hasNestingRestrictions){ // Select the optimized path for handling edge sets that don't have nesting restrictions associated with them.
handleEdgeList(edgeSet, name, production, resultLink, startLocation);
}else{
handleEdgeListWithRestrictions(edgeSet, name, production, resultLink, startLocation, filteredParents);
}
}
}
/**
* Handles reductions.
*/
private void handleEdgeList(EdgesSet<P> edgeSet, String name, P production, Link resultLink, int startLocation){
AbstractContainerNode<P> resultStore = null;
int resultStoreId = EdgesSet.DEFAULT_RESULT_STORE_ID;
if(edgeSet.getLastVisitedLevel(resultStoreId) != location){
AbstractStackNode<P> edge = edgeSet.get(0);
if(edge.isRecovered()){
resultStore = new RecoveredNode<P>(inputURI, startLocation, location);
}else if(edge.isExpandable()){
resultStore = new ExpandableContainerNode<P>(inputURI, startLocation, location, startLocation == location, edge.isSeparator(), edge.isLayout());
}else{
resultStore = new SortContainerNode<P>(inputURI, startLocation, location, startLocation == location, edge.isSeparator(), edge.isLayout());
}
stacksWithNonTerminalsToReduce.push(edge, resultStore);
if(debugListener != null) debugListener.reduced(edge);
for(int j = edgeSet.size() - 1; j >= 1; --j){
edge = edgeSet.get(j);
stacksWithNonTerminalsToReduce.push(edge, resultStore);
if(debugListener != null) debugListener.reduced(edge);
}
edgeSet.setLastVisitedLevel(location, resultStoreId);
edgeSet.setLastResult(resultStore, resultStoreId);
}else{
resultStore = edgeSet.getLastResult(resultStoreId);
}
resultStore.addAlternative(production, resultLink);
}
// Reuse these structures.
private final IntegerList firstTimeRegistration = new IntegerList();
private final IntegerList firstTimeReductions = new IntegerList();
/**
* Handles reductions which may be associated with nesting restrictions.
*/
private void handleEdgeListWithRestrictions(EdgesSet<P> edgeSet, String name, P production, Link resultLink, int startLocation, IntegerList filteredParents){
// Only add the result to each resultstore once.
// Make sure each edge only gets added to the non-terminal reduction list once per level, by keeping track of them.
firstTimeRegistration.clear();
firstTimeReductions.clear();
for(int j = edgeSet.size() - 1; j >= 0; --j){
AbstractStackNode<P> edge = edgeSet.get(j);
int resultStoreId = getResultStoreId(edge.getId());
if(!firstTimeReductions.contains(resultStoreId)){
if(firstTimeRegistration.contains(resultStoreId)){
if(debugListener != null) debugListener.filteredByNestingRestriction(edge);
continue;
}
firstTimeRegistration.add(resultStoreId);
// Check whether or not the nesting is allowed.
if(filteredParents == null || !filteredParents.contains(edge.getId())){
AbstractContainerNode<P> resultStore = null;
if(edgeSet.getLastVisitedLevel(resultStoreId) == location){
resultStore = edgeSet.getLastResult(resultStoreId);
}
if(resultStore == null){
if (edge.isRecovered()) {
resultStore = new RecoveredNode<P>(inputURI, startLocation, location);
}else if (edge.isExpandable()) {
resultStore = new ExpandableContainerNode<P>(inputURI, startLocation, location, startLocation == location, edge.isSeparator(), edge.isLayout());
}else {
resultStore = new SortContainerNode<P>(inputURI, startLocation, location, startLocation == location, edge.isSeparator(), edge.isLayout());
}
edgeSet.setLastVisitedLevel(location, resultStoreId);
edgeSet.setLastResult(resultStore, resultStoreId);
stacksWithNonTerminalsToReduce.push(edge, resultStore);
firstTimeReductions.add(resultStoreId);
}
resultStore.addAlternative(production, resultLink);
if(debugListener != null) debugListener.reduced(edge);
}else{
if(debugListener != null) debugListener.filteredByNestingRestriction(edge);
}
}else{
AbstractContainerNode<P> resultStore = edgeSet.getLastResult(resultStoreId);
stacksWithNonTerminalsToReduce.push(edge, resultStore);
}
}
}
/**
* Move to the next symbol(s) in the production.
*/
private void moveToNext(AbstractStackNode<P> node, AbstractNode result){
int nextDot = node.getDot() + 1;
AbstractStackNode<P>[] prod = node.getProduction();
AbstractStackNode<P> newNext = prod[nextDot];
AbstractStackNode<P> next = updateNextNode(newNext, node, result);
// Handle alternative continuations of this production (related to prefix-sharing).
AbstractStackNode<P>[][] alternateProds = node.getAlternateProductions();
if(alternateProds != null){
IntegerObjectList<EdgesSet<P>> edgesMap = null;
ArrayList<Link>[] prefixesMap = null;
if(next != null){
edgesMap = next.getEdges();
prefixesMap = next.getPrefixesMap();
}
for(int i = alternateProds.length - 1; i >= 0; --i){
prod = alternateProds[i];
if(nextDot == prod.length) continue;
AbstractStackNode<P> newAlternativeNext = prod[nextDot];
if(edgesMap != null){
updateAlternativeNextNode(newAlternativeNext, node, result, edgesMap, prefixesMap);
}else{
AbstractStackNode<P> alternativeNext = updateNextNode(newAlternativeNext, node, result);
if(alternativeNext != null){
edgesMap = alternativeNext.getEdges();
prefixesMap = alternativeNext.getPrefixesMap();
}
}
}
}
}
/**
* Progress to the next 'states' associated with the given node.
* I.e. move to the next symbol(s) in the production (if available) and executed reductions if necessary.
*/
private void move(AbstractStackNode<P> node, AbstractNode result){
if(debugListener != null) debugListener.moving(node, result);
// Handle filtering.
ICompletionFilter[] completionFilters = node.getCompletionFilters();
if(completionFilters != null){
int startLocation = node.getStartLocation();
for(int i = completionFilters.length - 1; i >= 0; --i){
if(completionFilters[i].isFiltered(input, startLocation, location, positionStore)){
filteredNodes.push(node, result);
if(debugListener != null) debugListener.filteredByCompletionFilter(node, result);
return;
}
}
}
if(node.isEndNode()){
if(!result.isEmpty() || node.getId() == AbstractExpandableStackNode.DEFAULT_LIST_EPSILON_ID){ // Only go into the nullable fix path for nullables (special list epsilons can be ignored as well).
updateEdges(node, result);
}else{
updateNullableEdges(node, result);
}
}
if(node.hasNext()){
moveToNext(node, result);
}
}
/**
* Initiate the handling of stacks.
*/
private void reduce(){
// Reduce terminals
while(!stacksWithTerminalsToReduce.isEmpty()){
move(stacksWithTerminalsToReduce.peekFirst(), stacksWithTerminalsToReduce.popSecond());
}
// Reduce non-terminals
while(!stacksWithNonTerminalsToReduce.isEmpty()){
move(stacksWithNonTerminalsToReduce.peekFirst(), stacksWithNonTerminalsToReduce.popSecond());
}
}
/**
* Locates the initial set of stacks that is queued for handling, for which the least amount of characters needs to be shifted.
*/
private boolean findFirstStacksToReduce(){
for(int i = 0; i < todoLists.length; ++i){
DoubleStack<AbstractStackNode<P>, AbstractNode> terminalsTodo = todoLists[i];
if(!(terminalsTodo == null || terminalsTodo.isEmpty())){
stacksWithTerminalsToReduce = terminalsTodo;
location += i;
queueIndex = i;
return true;
}
}
if (recoverer != null) {
DoubleArrayList<AbstractStackNode<P>, AbstractNode> recoveredNodes = recoverer.reviveStacks(input, location, unexpandableNodes, unmatchableLeafNodes, unmatchableMidProductionNodes, filteredNodes);
if (recoveredNodes.size() > 0) { // TODO Do something with the revived node. Is this the right location to do this?
for (int i = 0; i < recoveredNodes.size(); i++) {
AbstractStackNode<P> recovered = recoveredNodes.getFirst(i);
addTodo(recovered, recovered.getLength(), recoveredNodes.getSecond(i));
}
return findStacksToReduce();
}
parseErrorOccured = true;
}
return false;
}
/**
* Locates the set of stacks that is queued for handling, for which the least amount of characters needs to be shifted.
*/
private boolean findStacksToReduce(){
int queueDepth = todoLists.length;
for(int i = 1; i < queueDepth; ++i){
queueIndex = (queueIndex + 1) % queueDepth;
DoubleStack<AbstractStackNode<P>, AbstractNode> terminalsTodo = todoLists[queueIndex];
if(!(terminalsTodo == null || terminalsTodo.isEmpty())){
stacksWithTerminalsToReduce = terminalsTodo;
location += i;
return true;
}
}
if (recoverer != null) {
DoubleArrayList<AbstractStackNode<P>, AbstractNode> recoveredNodes = recoverer.reviveStacks(input, location, unexpandableNodes, unmatchableLeafNodes, unmatchableMidProductionNodes, filteredNodes);
if (recoveredNodes.size() > 0) {
for (int i = 0; i < recoveredNodes.size(); i++) {
AbstractStackNode<P> recovered = recoveredNodes.getFirst(i);
int levelsFromHere = recovered.getLength() - (location - recovered.getStartLocation());
addTodo(recovered, levelsFromHere, recoveredNodes.getSecond(i));
}
return findStacksToReduce();
}
parseErrorOccured = true;
}
return false;
}
public boolean parseErrorHasOccurred(){
return parseErrorOccured;
}
/**
* Inserts a stack bottom into the todo-list.
*/
@SuppressWarnings("unchecked")
private void addTodo(AbstractStackNode<P> node, int length, AbstractNode result){
if(result == null) throw new RuntimeException();
int queueDepth = todoLists.length;
if(length >= queueDepth){
DoubleStack<AbstractStackNode<P>, AbstractNode>[] oldTodoLists = todoLists;
todoLists = new DoubleStack[length + 1];
System.arraycopy(oldTodoLists, queueIndex, todoLists, 0, queueDepth - queueIndex);
System.arraycopy(oldTodoLists, 0, todoLists, queueDepth - queueIndex, queueIndex);
queueDepth = length + 1;
queueIndex = 0;
}
int insertLocation = (queueIndex + length) % queueDepth;
DoubleStack<AbstractStackNode<P>, AbstractNode> terminalsTodo = todoLists[insertLocation];
if(terminalsTodo == null){
terminalsTodo = new DoubleStack<AbstractStackNode<P>, AbstractNode>();
todoLists[insertLocation] = terminalsTodo;
}
terminalsTodo.push(node, result);
}
/**
* Handles the retrieved alternatives for the given stack.
*/
private boolean handleExpects(AbstractStackNode<P>[] expects, EdgesSet<P> cachedEdges, AbstractStackNode<P> stackBeingWorkedOn){
boolean hasValidAlternatives = false;
sharedLastExpects.dirtyClear();
EXPECTS: for(int i = expects.length - 1; i >= 0; --i){
AbstractStackNode<P> first = expects[i];
if(first.isMatchable()){ // Eager matching optimization.
int length = first.getLength();
int endLocation = location + length;
if(endLocation > input.length) continue;
AbstractNode result = first.match(input, location);
if(result == null){
unmatchableLeafNodes.push(first);
if(debugListener != null) debugListener.failedToMatch(first);
continue;
}
if(debugListener != null) debugListener.matched(first, result);
// Handle filtering.
IEnterFilter[] enterFilters = first.getEnterFilters();
if(enterFilters != null){
for(int j = enterFilters.length - 1; j >= 0; --j){
if(enterFilters[j].isFiltered(input, location, positionStore)){
if(debugListener != null) debugListener.filteredByEnterFilter(first);
continue EXPECTS;
}
}
}
first = first.getCleanCopyWithResult(location, result);
addTodo(first, length, result);
}else{
first = first.getCleanCopy(location);
stacksToExpand.push(first);
}
first.initEdges();
first.addEdges(cachedEdges, location);
sharedLastExpects.add(first.getId(), first);
hasValidAlternatives = true;
if(debugListener != null) debugListener.expanded(stackBeingWorkedOn, first);
}
return hasValidAlternatives;
}
/**
* Check whether or not the given sort name has nesting restrictions associated with it.
*/
protected boolean hasNestingRestrictions(String name){
return false; // Priority and associativity filtering is off by default.
}
/**
* Retrieves the set of disallowed parents for the given child.
*/
protected IntegerList getFilteredParents(int childId){
return null; // Default implementation; intended to be overwritten in sub-classes.
}
/**
* Retrieves the resultstore id associated with the given id.
*/
protected int getResultStoreId(int id){
return EdgesSet.DEFAULT_RESULT_STORE_ID; // Default implementation; intended to be overwritten in sub-classes.
}
/**
* Expands the given stack node.
*/
private void expandStack(AbstractStackNode<P> stack){
if(debugListener != null) debugListener.expanding(stack);
// Handle filtering.
IEnterFilter[] enterFilters = stack.getEnterFilters();
if(enterFilters != null){
for(int i = enterFilters.length - 1; i >= 0; --i){
if(enterFilters[i].isFiltered(input, location, positionStore)){
unexpandableNodes.push(stack);
if(debugListener != null) debugListener.filteredByEnterFilter(stack);
return;
}
}
}
if(stack.isMatchable()){ // Eager matching optimization related.
addTodo(stack, stack.getLength(), stack.getResult());
}else if(!stack.isExpandable()){ // A 'normal' non-terminal.
EdgesSet<P> cachedEdges = cachedEdgesForExpect.get(stack.getName());
if(cachedEdges == null){
cachedEdges = new EdgesSet<P>(1);
cachedEdgesForExpect.put(stack.getName(), cachedEdges);
AbstractStackNode<P>[] expects = invokeExpects(stack);
if(expects == null){
unexpandableNodes.push(stack);
return;
}
if(!handleExpects(expects, cachedEdges, stack)){
unexpandableNodes.push(stack);
return;
}
}else{
int resultStoreId = getResultStoreId(stack.getId());
if(cachedEdges.getLastVisitedLevel(resultStoreId) == location){ // Is nullable, add the known results.
stacksWithNonTerminalsToReduce.push(stack, cachedEdges.getLastResult(resultStoreId));
if(debugListener != null) debugListener.foundIterationCachedNullableResult(stack);
}
}
cachedEdges.add(stack);
stack.setIncomingEdges(cachedEdges);
}else{ // Expandable
EdgesSet<P> cachedEdges = cachedEdgesForExpect.get(stack.getName());
if(cachedEdges == null){
boolean expanded = false;
cachedEdges = new EdgesSet<P>();
cachedEdgesForExpect.put(stack.getName(), cachedEdges);
AbstractStackNode<P>[] listChildren = stack.getChildren();
CHILDREN : for(int i = listChildren.length - 1; i >= 0; --i){
AbstractStackNode<P> child = listChildren[i];
int childId = child.getId();
IntegerKeyedDoubleValueHashMap.Entry<AbstractStackNode<P>, DoubleArrayList<AbstractStackNode<P>, AbstractNode>> sharedChildEntry = sharedNextNodes.get(childId);
if(sharedChildEntry != null && sharedChildEntry.value2 == null){
AbstractStackNode<P> sharedChild = sharedChildEntry.value1;
sharedChild.setEdgesSetWithPrefix(cachedEdges, null, location);
}else{
if(child.isMatchable()){
int length = child.getLength();
int endLocation = location + length;
if(endLocation > input.length) continue;
AbstractNode result = child.match(input, location);
if(result == null){
unmatchableLeafNodes.push(child);
if(debugListener != null) debugListener.failedToMatch(child);
continue;
}
if(debugListener != null) debugListener.matched(child, result);
// Handle filtering
IEnterFilter[] childEnterFilters = child.getEnterFilters();
if(childEnterFilters != null){
for(int j = childEnterFilters.length - 1; j >= 0; --j){
if(childEnterFilters[j].isFiltered(input, location, positionStore)) {
if(debugListener != null) debugListener.filteredByEnterFilter(child);
continue CHILDREN;
}
}
}
child = child.getCleanCopyWithResult(location, result);
addTodo(child, length, result);
}else{
child = child.getCleanCopy(location);
stacksToExpand.push(child);
}
child.initEdges();
child.setEdgesSetWithPrefix(cachedEdges, null, location);
sharedNextNodes.putUnsafe(childId, child, null);
if(debugListener != null) debugListener.expanded(stack, child);
}
expanded = true;
}
if(stack.canBeEmpty()){ // Star list or optional.
AbstractStackNode<P> empty = stack.getEmptyChild().getCleanCopyWithResult(location, EpsilonStackNode.EPSILON_RESULT);
empty.initEdges();
empty.addEdges(cachedEdges, location);
stacksToExpand.push(empty);
if(debugListener != null) debugListener.expanded(stack, empty);
expanded = true;
}
if(!expanded){
unexpandableNodes.push(stack);
}
}
int resultStoreId = getResultStoreId(stack.getId());
if(cachedEdges.getLastVisitedLevel(resultStoreId) == location){ // Is nullable, add the known results.
stacksWithNonTerminalsToReduce.push(stack, cachedEdges.getLastResult(resultStoreId));
if(debugListener != null) debugListener.foundIterationCachedNullableResult(stack);
}
cachedEdges.add(stack);
stack.setIncomingEdges(cachedEdges);
}
}
/**
* Initiate stack expansion for all queued stacks.
*/
private void expand(){
while(!stacksToExpand.isEmpty()){
expandStack(stacksToExpand.pop());
}
}
protected AbstractNode parse(AbstractStackNode<P> startNode, URI inputURI, int[] input){
return parse(startNode, inputURI, input, (IRecoverer<P>) null, (IDebugListener<P>) null);
}
/**
* Initiates parsing.
*/
@SuppressWarnings("unchecked")
protected AbstractNode parse(AbstractStackNode<P> startNode, URI inputURI, int[] input, IRecoverer<P> recoverer, IDebugListener<P> debugListener){
initTime();
try {
if(invoked){
throw new RuntimeException("Can only invoke 'parse' once.");
}
invoked = true;
// Initialize.
this.inputURI = inputURI;
this.input = input;
this.recoverer = recoverer;
this.debugListener = debugListener;
// Initialzed the position store.
positionStore.index(input);
todoLists = new DoubleStack[DEFAULT_TODOLIST_CAPACITY];
// Handle the initial expansion of the root node.
AbstractStackNode<P> rootNode = startNode;
rootNode.initEdges();
stacksToExpand.push(rootNode);
lookAheadChar = (input.length > 0) ? input[0] : 0;
if(debugListener != null) debugListener.shifting(location, input, positionStore);
expand();
if(findFirstStacksToReduce()){
boolean shiftedLevel = (location != 0);
do{
lookAheadChar = (location < input.length) ? input[location] : 0;
if(shiftedLevel){ // Nullable fix for the first level.
sharedNextNodes.clear();
cachedEdgesForExpect.clear();
unexpandableNodes.dirtyClear();
unmatchableLeafNodes.dirtyClear();
unmatchableMidProductionNodes.dirtyClear();
filteredNodes.dirtyClear();
if(debugListener != null) debugListener.shifting(location, input, positionStore);
}
// Reduce-expand loop.
do{
if(debugListener != null) debugListener.iterating();
reduce();
expand();
}while(!stacksWithNonTerminalsToReduce.isEmpty() || !stacksWithTerminalsToReduce.isEmpty());
shiftedLevel = true;
}while(findStacksToReduce());
}
// Check if we were successful.
if(location == input.length){
EdgesSet<P> startNodeEdgesSet = startNode.getIncomingEdges();
int resultStoreId = getResultStoreId(startNode.getId());
if(startNodeEdgesSet != null && startNodeEdgesSet.getLastVisitedLevel(resultStoreId) == input.length){
// Parsing succeeded.
return startNodeEdgesSet.getLastResult(resultStoreId); // Success.
}
}
}
finally {
checkTime("Parsing");
}
try {
// A parse error occured, and recovery failed as well
parseErrorOccured = true;
int errorLocation = (location == Integer.MAX_VALUE ? 0 : location);
int line = positionStore.findLine(errorLocation);
int column = positionStore.getColumn(errorLocation, line);
if (location == input.length) {
throw new ParseError("Parse error", inputURI, errorLocation, 0, line, line, column, column, (Stack<AbstractStackNode<?>>) (Stack<?>) unexpandableNodes, (Stack<AbstractStackNode<?>>) (Stack<?>) unmatchableLeafNodes, (DoubleStack<ArrayList<AbstractStackNode<?>>, AbstractStackNode<?>>) (DoubleStack<?, ?>) unmatchableMidProductionNodes, (DoubleStack<AbstractStackNode<?>, AbstractNode>) (DoubleStack<?, ?>) filteredNodes);
}
throw new ParseError("Parse error", inputURI, errorLocation, 1, line, line, column, column + 1, (Stack<AbstractStackNode<?>>) (Stack<?>) unexpandableNodes, (Stack<AbstractStackNode<?>>) (Stack<?>) unmatchableLeafNodes, (DoubleStack<ArrayList<AbstractStackNode<?>>, AbstractStackNode<?>>) (DoubleStack<?, ?>) unmatchableMidProductionNodes, (DoubleStack<AbstractStackNode<?>, AbstractNode>) (DoubleStack<?, ?>) filteredNodes);
}
finally {
checkTime("Error handling");
}
}
private void initTime() {
timestamp = System.nanoTime();
}
private void checkTime(String msg) {
long newStamp = System.nanoTime();
long duration = newStamp - timestamp;
timestamp = newStamp;
if (printTimes) {
System.err.println(msg + ": " + duration / (1000 * 1000));
}
}
private static int[] charsToInts(char[] input){
int[] result = new int[Character.codePointCount(input, 0, input.length)];
int j = 0;
for(int i = 0; i < input.length; i++){
if (!Character.isLowSurrogate(input[i])) {
result[j++] = Character.codePointAt(input, i);
}
}
return result;
}
/**
* Parses with post parse filtering.
*/
private T parse(String nonterminal, URI inputURI, int[] input, IActionExecutor<T> actionExecutor, INodeFlattener<T, S> converter, INodeConstructorFactory<T, S> nodeConstructorFactory, IRecoverer<P> recoverer, IDebugListener<P> debugListener){
AbstractNode result = parse(new NonTerminalStackNode<P>(AbstractStackNode.START_SYMBOL_ID, 0, nonterminal), inputURI, input, recoverer, debugListener);
return buildResult(result, converter, nodeConstructorFactory, actionExecutor);
}
public T parse(String nonterminal, URI inputURI, char[] input, IActionExecutor<T> actionExecutor, INodeFlattener<T, S> converter, INodeConstructorFactory<T, S> nodeConstructorFactory, IRecoverer<P> recoverer, IDebugListener<P> debugListener){
return parse(nonterminal, inputURI, charsToInts(input), actionExecutor, converter, nodeConstructorFactory, recoverer, debugListener);
}
public T parse(String nonterminal, URI inputURI, char[] input, IActionExecutor<T> actionExecutor, INodeFlattener<T, S> converter, INodeConstructorFactory<T, S> nodeConstructorFactory, IRecoverer<P> recoverer){
return parse(nonterminal, inputURI, input, actionExecutor, converter, nodeConstructorFactory, recoverer, null);
}
public T parse(String nonterminal, URI inputURI, char[] input, IActionExecutor<T> actionExecutor, INodeFlattener<T, S> converter, INodeConstructorFactory<T, S> nodeConstructorFactory, IDebugListener<P> debugListener){
return parse(nonterminal, inputURI, input, actionExecutor, converter, nodeConstructorFactory, null, debugListener);
}
public T parse(String nonterminal, URI inputURI, char[] input, IActionExecutor<T> actionExecutor, INodeFlattener<T, S> converter, INodeConstructorFactory<T, S> nodeConstructorFactory){
return parse(nonterminal, inputURI, input, actionExecutor, converter, nodeConstructorFactory, null, null);
}
/**
* Parses without post parse filtering.
*/
private T parse(String nonterminal, URI inputURI, int[] input, INodeFlattener<T, S> converter, INodeConstructorFactory<T, S> nodeConstructorFactory, IRecoverer<P> recoverer, IDebugListener<P> debugListener){
AbstractNode result = parse(new NonTerminalStackNode<P>(AbstractStackNode.START_SYMBOL_ID, 0, nonterminal), inputURI, input, recoverer, debugListener);
return buildResult(result, converter, nodeConstructorFactory, new VoidActionExecutor<T>());
}
public T parse(String nonterminal, URI inputURI, char[] input, INodeFlattener<T, S> converter, INodeConstructorFactory<T, S> nodeConstructorFactory, IRecoverer<P> recoverer, IDebugListener<P> debugListener){
return parse(nonterminal, inputURI, charsToInts(input), converter, nodeConstructorFactory, recoverer, debugListener);
}
public T parse(String nonterminal, URI inputURI, char[] input, INodeFlattener<T, S> converter, INodeConstructorFactory<T, S> nodeConstructorFactory, IRecoverer<P> recoverer){
return parse(nonterminal, inputURI, input, converter, nodeConstructorFactory, recoverer, null);
}
public T parse(String nonterminal, URI inputURI, char[] input, INodeFlattener<T, S> converter, INodeConstructorFactory<T, S> nodeConstructorFactory, IDebugListener<P> debugListener){
return parse(nonterminal, inputURI, input, converter, nodeConstructorFactory, null, debugListener);
}
public T parse(String nonterminal, URI inputURI, char[] input, INodeFlattener<T, S> converter, INodeConstructorFactory<T, S> nodeConstructorFactory) {
return parse(nonterminal, inputURI, charsToInts(input), converter, nodeConstructorFactory, null, null);
}
protected T parse(AbstractStackNode<P> startNode, URI inputURI, char[] input, INodeFlattener<T, S> converter, INodeConstructorFactory<T, S> nodeConstructorFactory) {
AbstractNode result = parse(startNode, inputURI, charsToInts(input), null, null);
return buildResult(result, converter, nodeConstructorFactory, new VoidActionExecutor<T>());
}
/**
* Constructed the final parse result using the given converter.
*/
protected T buildResult(AbstractNode result, INodeFlattener<T, S> converter, INodeConstructorFactory<T, S> nodeConstructorFactory, IActionExecutor<T> actionExecutor){
initTime();
try {
FilteringTracker filteringTracker = new FilteringTracker();
// Invoke the forest flattener, a.k.a. "the bulldozer".
Object rootEnvironment = actionExecutor != null ? actionExecutor.createRootEnvironment() : null;
T parseResult = null;
try {
parseResult = converter.convert(nodeConstructorFactory, result, positionStore, filteringTracker, actionExecutor, rootEnvironment);
}
finally {
actionExecutor.completed(rootEnvironment, (parseResult == null));
}
if(parseResult != null){
return parseResult; // Success.
}
int offset = filteringTracker.getOffset();
int endOffset = filteringTracker.getEndOffset();
int length = endOffset - offset;
int beginLine = positionStore.findLine(offset);
int beginColumn = positionStore.getColumn(offset, beginLine);
int endLine = positionStore.findLine(endOffset);
int endColumn = positionStore.getColumn(endOffset, endLine);
throw new ParseError("All results were filtered", inputURI, offset, length, beginLine, endLine, beginColumn, endColumn);
}
finally {
checkTime("Unbinarizing, post-parse filtering, and mapping to UPTR");
}
}
}