/******************************************************************************* * 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.result.out; import java.net.URI; import org.rascalmpl.parser.gtd.location.PositionStore; import org.rascalmpl.parser.gtd.result.AbstractNode; import org.rascalmpl.parser.gtd.result.SortContainerNode; import org.rascalmpl.parser.gtd.result.action.IActionExecutor; import org.rascalmpl.parser.gtd.result.out.INodeFlattener.CycleMark; import org.rascalmpl.parser.gtd.result.struct.Link; import org.rascalmpl.parser.gtd.util.ArrayList; import org.rascalmpl.parser.gtd.util.ForwardLink; import org.rascalmpl.parser.gtd.util.IndexedStack; import org.rascalmpl.parser.gtd.util.IntegerKeyedHashMap; import org.rascalmpl.parser.gtd.util.ObjectIntegerKeyedHashMap; import org.rascalmpl.parser.gtd.util.ObjectIntegerKeyedHashSet; /** * A converter for sort container result nodes. */ public class SortContainerNodeFlattener<P, T, S>{ @SuppressWarnings("unchecked") private final static ForwardLink<AbstractNode> NO_NODES = ForwardLink.TERMINATOR; private final IntegerKeyedHashMap<ObjectIntegerKeyedHashMap<Object, T>> preCache; private final IntegerKeyedHashMap<ObjectIntegerKeyedHashSet<T>> cache; public SortContainerNodeFlattener(){ super(); preCache = new IntegerKeyedHashMap<ObjectIntegerKeyedHashMap<Object, T>>(); cache = new IntegerKeyedHashMap<ObjectIntegerKeyedHashSet<T>>(); } /** * Gather all the alternatives ending with the given child. */ private void gatherAlternatives(INodeFlattener<T, S> converter, INodeConstructorFactory<T, S> nodeConstructorFactory, Link child, ArrayList<T> gatheredAlternatives, Object production, IndexedStack<AbstractNode> stack, int depth, CycleMark cycleMark, PositionStore positionStore, S sourceLocation, int offset, int endOffset, FilteringTracker filteringTracker, IActionExecutor<T> actionExecutor, Object environment){ AbstractNode resultNode = child.getNode(); if(!(resultNode.isEpsilon() && child.getPrefixes() == null)){ // Has non-epsilon results. gatherProduction(converter, nodeConstructorFactory, child, new ForwardLink<AbstractNode>(NO_NODES, resultNode), gatheredAlternatives, production, stack, depth, cycleMark, positionStore, sourceLocation, offset, endOffset, filteringTracker, actionExecutor, environment); }else{ // Has a single epsilon result. buildAlternative(converter, nodeConstructorFactory, NO_NODES, gatheredAlternatives, production, stack, depth, cycleMark, positionStore, sourceLocation, offset, endOffset, filteringTracker, actionExecutor, environment); } } /** * Gathers all alternatives for the given production related to the given child and postfix. */ private void gatherProduction(INodeFlattener<T, S> converter, INodeConstructorFactory<T, S> nodeConstructorFactory, Link child, ForwardLink<AbstractNode> postFix, ArrayList<T> gatheredAlternatives, Object production, IndexedStack<AbstractNode> stack, int depth, CycleMark cycleMark, PositionStore positionStore, S sourceLocation, int offset, int endOffset, FilteringTracker filteringTracker, IActionExecutor<T> actionExecutor, Object environment){ ArrayList<Link> prefixes = child.getPrefixes(); if(prefixes == null){ // Reached the start of the production. buildAlternative(converter, nodeConstructorFactory, postFix, gatheredAlternatives, production, stack, depth, cycleMark, positionStore, sourceLocation, offset, endOffset, filteringTracker, actionExecutor, environment); return; } for(int i = prefixes.size() - 1; i >= 0; --i){ // Traverse all the prefixes (can be more then one in case of ambiguity). Link prefix = prefixes.get(i); gatherProduction(converter, nodeConstructorFactory, prefix, new ForwardLink<AbstractNode>(postFix, prefix.getNode()), gatheredAlternatives, production, stack, depth, cycleMark, positionStore, sourceLocation, offset, endOffset, filteringTracker, actionExecutor, environment); } } /** * Construct the UPTR representation for the given production. * Additionally, it handles all semantic actions related 'events' associated with it. */ private void buildAlternative(INodeFlattener<T, S> converter, INodeConstructorFactory<T, S> nodeConstructorFactory, ForwardLink<AbstractNode> postFix, ArrayList<T> gatheredAlternatives, Object production, IndexedStack<AbstractNode> stack, int depth, CycleMark cycleMark, PositionStore positionStore, S sourceLocation, int offset, int endOffset, FilteringTracker filteringTracker, IActionExecutor<T> actionExecutor, Object environment){ Object newEnvironment = actionExecutor.enteringProduction(production, environment); // Fire a 'entering production' event to enable environment handling. int postFixLength = postFix.length; ArrayList<T> children = new ArrayList<T>(); for(int i = 0; i < postFixLength; ++i){ AbstractNode node = postFix.element; postFix = postFix.next; newEnvironment = actionExecutor.enteringNode(production, i, newEnvironment); // Fire a 'entering node' event when converting a child to enable environment handling. T constructedNode = converter.convert(nodeConstructorFactory, node, stack, depth, cycleMark, positionStore, filteringTracker, actionExecutor, environment); if(constructedNode == null){ actionExecutor.exitedProduction(production, true, newEnvironment); // Filtered. return; } children.add(constructedNode); } T result = nodeConstructorFactory.createSortNode(children, production); if(sourceLocation != null) result = nodeConstructorFactory.addPositionInformation(result, sourceLocation); // Add location information (if available). result = actionExecutor.filterProduction(result, environment); // Execute the semantic actions associated with this node. if(result == null){ filteringTracker.setLastFiltered(offset, endOffset); actionExecutor.exitedProduction(production, true, environment); // Filtered. return; } // TODO: what about if somebody build a tree without a new location? gatheredAlternatives.add(result); actionExecutor.exitedProduction(production, false, environment); // Successful construction. } /** * Converts the given sort container result node to the UPTR format. */ public T convertToUPTR(INodeFlattener<T, S> converter, INodeConstructorFactory<T, S> nodeConstructorFactory, SortContainerNode<P> node, IndexedStack<AbstractNode> stack, int depth, CycleMark cycleMark, PositionStore positionStore, FilteringTracker filteringTracker, IActionExecutor<T> actionExecutor, Object environment){ int offset = node.getOffset(); int endOffset = node.getEndOffset(); Object firstProduction = node.getFirstProduction(); Object rhs = nodeConstructorFactory.getRhs(node.getFirstProduction()); boolean hasSideEffects = actionExecutor.isImpure(rhs); if(depth <= cycleMark.depth){ // Only check for sharing if we are not currently inside a cycle. if(!hasSideEffects){ // If this sort node and its direct and indirect children do not rely on side-effects from semantic actions, check the cache for existing results. ObjectIntegerKeyedHashMap<Object, T> levelCache = preCache.get(offset); if(levelCache != null){ T cachedResult = levelCache.get(rhs, endOffset); if(cachedResult != null){ return cachedResult; } } } cycleMark.reset(); } S sourceLocation = null; URI input = node.getInput(); if(!(node.isLayout() || input == null)){ // Construct a source location annotation if this sort container does not represent a layout non-terminal and if it's available. sourceLocation = nodeConstructorFactory.createPositionInformation(input, offset, endOffset, positionStore); } int index = stack.contains(node); if(index != -1){ // Cycle detected. T cycle = nodeConstructorFactory.createCycleNode(depth - index, firstProduction); cycle = actionExecutor.filterCycle(cycle, environment); if(cycle != null){ if(sourceLocation != null) cycle = nodeConstructorFactory.addPositionInformation(cycle, sourceLocation); }else{ filteringTracker.setLastFiltered(offset, endOffset); } cycleMark.setMark(index); return cycle; } int childDepth = depth + 1; stack.push(node, depth); // Push this node on the stack. // Gather the alternatives. ArrayList<T> gatheredAlternatives = new ArrayList<T>(); gatherAlternatives(converter, nodeConstructorFactory, node.getFirstAlternative(), gatheredAlternatives, firstProduction, stack, childDepth, cycleMark, positionStore, sourceLocation, offset, endOffset, filteringTracker, actionExecutor, environment); ArrayList<Link> alternatives = node.getAdditionalAlternatives(); ArrayList<P> productions = node.getAdditionalProductions(); if(alternatives != null){ for(int i = alternatives.size() - 1; i >= 0; --i){ gatherAlternatives(converter, nodeConstructorFactory, alternatives.get(i), gatheredAlternatives, productions.get(i), stack, childDepth, cycleMark, positionStore, sourceLocation, offset, endOffset, filteringTracker, actionExecutor, environment); } } // Construct the resulting tree containing all gathered alternatives. T result = null; int nrOfAlternatives = gatheredAlternatives.size(); if (nrOfAlternatives == 1) { // Not ambiguous. result = gatheredAlternatives.get(0); } else if (nrOfAlternatives > 0) { // Ambiguous. result = nodeConstructorFactory.createAmbiguityNode(gatheredAlternatives); result = actionExecutor.filterAmbiguity(result, environment); if(result != null){ if(sourceLocation != null){ result = nodeConstructorFactory.addPositionInformation(result, sourceLocation); } }else{ filteringTracker.setLastFiltered(offset, endOffset); } } stack.dirtyPurge(); // Pop this node off the stack. if(result != null && depth < cycleMark.depth){ // Only share the constructed tree if we are not in a cycle. if(!hasSideEffects){ // Cache side-effect free tree. ObjectIntegerKeyedHashMap<Object, T> levelCache = preCache.get(offset); if(levelCache != null){ T cachedResult = levelCache.get(rhs, endOffset); if(cachedResult != null){ return cachedResult; } levelCache.putUnsafe(rhs, endOffset, result); return result; } levelCache = new ObjectIntegerKeyedHashMap<Object, T>(); levelCache.putUnsafe(rhs, endOffset, result); preCache.put(offset, levelCache); }else{ // Cache tree with side-effects. ObjectIntegerKeyedHashSet<T> levelCache = cache.get(offset); if(levelCache != null){ T cachedResult = levelCache.getEquivalent(result, endOffset); if(cachedResult != null){ return cachedResult; } levelCache.putUnsafe(result, endOffset); return result; } levelCache = new ObjectIntegerKeyedHashSet<T>(); levelCache.putUnsafe(result, endOffset); cache.putUnsafe(offset, levelCache); } } return result; } }