/******************************************************************************* * Copyright (c) 2011-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.preprocessing; import java.util.Iterator; import org.rascalmpl.parser.gtd.stack.AbstractStackNode; import org.rascalmpl.parser.gtd.util.DoubleArrayList; import org.rascalmpl.parser.gtd.util.IntegerMap; import org.rascalmpl.parser.gtd.util.ObjectIntegerKeyedHashMap; import org.rascalmpl.parser.gtd.util.SortedIntegerObjectList; /** * A preprocessor for building expect matrixes. * This preprocessor incorporates prefix-sharing into related alternatives, * where possible. It also initialized the 'static' versions of the stack nodes. */ @SuppressWarnings("cast") public class ExpectBuilder<P>{ private final IntegerMap resultStoreMappings; private final SortedIntegerObjectList<DoubleArrayList<P, AbstractStackNode<P>[]>> alternatives; public ExpectBuilder(IntegerMap resultStoreMappings){ super(); this.resultStoreMappings = resultStoreMappings; alternatives = new SortedIntegerObjectList<DoubleArrayList<P, AbstractStackNode<P>[]>>(); } /** * Registers the given alternative with this builder. */ @SuppressWarnings("unchecked") public void addAlternative(P production, AbstractStackNode<P>... alternative){ int alternativeLength = alternative.length; DoubleArrayList<P, AbstractStackNode<P>[]> alternativesList = alternatives.findValue(alternativeLength); if(alternativesList == null){ alternativesList = new DoubleArrayList<P, AbstractStackNode<P>[]>(); alternatives.add(alternativeLength, alternativesList); } // Clone the alternative so we don't get entangled in 'by-reference' related issues. AbstractStackNode<P>[] clonedAlternative = (AbstractStackNode<P>[]) new AbstractStackNode[alternativeLength]; for(int i = alternativeLength - 1; i >= 0; --i){ clonedAlternative[i] = alternative[i].getCleanCopy(AbstractStackNode.DEFAULT_START_LOCATION); } alternativesList.add(production, clonedAlternative); } /** * This method is intended to be overwritten by subclasses. In offers the * possibility to prevent certain productions from being prefix shared. */ protected boolean isSharable(P production){ return true; // Default implementation } /** * Constructs and initializes the expect array and calculates * prefix-sharing. */ @SuppressWarnings("unchecked") public AbstractStackNode<P>[] buildExpectArray(){ ObjectIntegerKeyedHashMap<AbstractStackNode<P>, AbstractStackNode<P>[]> constructedExpects = new ObjectIntegerKeyedHashMap<AbstractStackNode<P>, AbstractStackNode<P>[]>(); for(int i = alternatives.size() - 1; i >= 0; --i){ // Walk over the list of alternatives, starting at the longest ones (this reduces the complexity of the sharing calculation). DoubleArrayList<P, AbstractStackNode<P>[]> alternativesList = alternatives.getValue(i); for(int j = alternativesList.size() - 1; j >= 0; --j){ AbstractStackNode<P>[] sharedExpect = null; P production = alternativesList.getFirst(j); AbstractStackNode<P>[] alternative = alternativesList.getSecond(j); AbstractStackNode<P> first = alternative[0]; int firstItemResultStoreId = resultStoreMappings.get(first.getId()); if(isSharable(production)){ // Check if the first symbol in the alternative, with the same nesting restrictions, has been encountered before in another alternative. sharedExpect = constructedExpects.get(first, firstItemResultStoreId); } if(sharedExpect == null){ // Not shared. // Initialize and register. alternative[alternative.length - 1].setProduction(alternative); alternative[alternative.length - 1].setAlternativeProduction(production); for(int k = alternative.length - 2; k >= 0; --k){ alternative[k].setProduction(alternative); } constructedExpects.putUnsafe(first, firstItemResultStoreId, alternative); }else{ // Shared. // Find the alternative with which the maximal amount of sharing is possible. int k = 1; CHAIN: for(; k < alternative.length; ++k){ AbstractStackNode<P> alternativeItem = alternative[k]; int alternativeItemResultStoreId = resultStoreMappings.get(alternativeItem.getId()); AbstractStackNode<P> sharedExpectItem = sharedExpect[k]; // Can't share the current alternative's symbol with the shared alternative we are currently matching against; try all other possible continuations to find a potential match. if(!alternativeItem.isEqual(sharedExpectItem) || alternativeItemResultStoreId != resultStoreMappings.get(sharedExpectItem.getId())){ AbstractStackNode<P>[][] otherSharedExpects = sharedExpectItem.getAlternateProductions(); if(otherSharedExpects != null){ for(int l = otherSharedExpects.length - 1; l >= 0; --l){ AbstractStackNode<P>[] otherSharedExpect = otherSharedExpects[l]; AbstractStackNode<P> otherSharedExpectItem = otherSharedExpect[k]; if(otherSharedExpectItem.isEqual(alternativeItem) && alternativeItemResultStoreId == resultStoreMappings.get(otherSharedExpectItem.getId())){ sharedExpect = otherSharedExpect; continue CHAIN; // Found a alternative continuation that matched. } } } break; // Did not find a alternative continuation that matched. } alternative[k] = sharedExpect[k]; // Remove 'garbage'. } if(k < alternative.length){ sharedExpect[k - 1].addProduction(alternative); // Add the current alternative as alternative continuation at the latest point at which it matched the shared alternative. // Initialize the tail of the current alternative. for(; k < alternative.length; ++k){ alternative[k].setProduction(alternative); } alternative[alternative.length - 1].setAlternativeProduction(production); }else{ sharedExpect[alternative.length - 1].setAlternativeProduction(production); // The current alternative was shorter them the alternative we are sharing with, so mark the appropriate symbol as end node (end nodes can still have next nodes). } } } } // Build the expect array. This array contains all unique 'first' nodes of the registered alternatives. int nrOfConstructedExpects = constructedExpects.size(); AbstractStackNode<P>[] expectArray = (AbstractStackNode<P>[]) new AbstractStackNode[nrOfConstructedExpects]; Iterator<AbstractStackNode<P>[]> constructedExpectsIterator = constructedExpects.valueIterator(); int i = nrOfConstructedExpects; while(constructedExpectsIterator.hasNext()){ expectArray[--i] = constructedExpectsIterator.next()[0]; } return expectArray; } }