
org.eclipse.xtext.grammaranalysis.impl.AbstractPDAProvider Maven / Gradle / Ivy
/*******************************************************************************
* Copyright (c) 2011 itemis AG (http://www.itemis.eu) and others.
* 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
*******************************************************************************/
package org.eclipse.xtext.grammaranalysis.impl;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.eclipse.emf.ecore.EClass;
import org.eclipse.xtext.AbstractElement;
import org.eclipse.xtext.Action;
import org.eclipse.xtext.GrammarUtil;
import org.eclipse.xtext.grammaranalysis.INFAState;
import org.eclipse.xtext.grammaranalysis.IPDAProvider;
import org.eclipse.xtext.grammaranalysis.IPDAState;
import org.eclipse.xtext.grammaranalysis.IPDAState.PDAStateType;
import org.eclipse.xtext.util.Pair;
import org.eclipse.xtext.util.Tuples;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
/**
* @author Moritz Eysholdt - Initial contribution and API
*/
public abstract class AbstractPDAProvider implements IPDAProvider {
protected static class PDAContext {
protected CTX context;
protected Map, PDAState> elements = Maps.newHashMap();
protected Map, PDAState> ruleCallEnter = Maps.newHashMap();
protected Map, PDAState> ruleCallExit = Maps.newHashMap();
protected PDAState start;
protected PDAState stop;
/**
* @since 2.0
*/
public PDAContext(CTX context) {
super();
this.context = context;
}
protected void setStart(PDAState start) {
this.start = start;
}
protected void setStop(PDAState stop) {
this.stop = stop;
}
}
protected static class PDAState implements IPDAState {
protected AbstractElement element;
protected Set followers = Collections.emptySet();
protected PDAStateType type;
public PDAState(PDAStateType type, AbstractElement element) {
super();
this.type = type;
this.element = element;
}
public Collection getFollowers() {
return followers;
}
public AbstractElement getGrammarElement() {
return element;
}
public PDAStateType getType() {
return type;
}
@Override
public String toString() {
if (type == null)
return "(type is null)";
GrammarElementTitleSwitch title = new GrammarElementTitleSwitch().showAssignments();
switch (type) {
case ELEMENT:
return title.doSwitch(element);
case RULECALL_ENTER:
return ">>" + title.doSwitch(element);
case RULECALL_EXIT:
return "<<" + title.doSwitch(element);
case START:
return "start";
case STOP:
return "stop";
}
return super.toString();
}
}
protected static class RuleCallStackElement {
protected RuleCallStackElement parent;
protected INFAState, ?> ruleCall;
protected boolean stopCheck = false;
public RuleCallStackElement(RuleCallStackElement parent, INFAState, ?> ruleCall) {
super();
this.parent = parent;
this.ruleCall = ruleCall;
}
protected RuleCallStackElement getParent() {
return parent;
}
public boolean hasEnteredTwice(INFAState, ?> element) {
RuleCallStackElement e = this;
int count = 0;
while (e != null) {
if (e.ruleCall == element) {
count++;
if (count >= 2)
return true;
}
e = e.parent;
}
return false;
}
public RuleCallStackElement cloneWithoutVisited() {
RuleCallStackElement result = new RuleCallStackElement(parent, ruleCall);
result.stopCheck = true;
return result;
}
protected INFAState, ?> getRuleCall() {
return ruleCall;
}
@Override
public String toString() {
List result = Lists.newArrayList();
RuleCallStackElement e = this;
while (e != null) {
result.add((e.stopCheck ? "!" : "") + e.getRuleCall());
e = e.parent;
}
return result.toString();
}
}
protected abstract boolean canEnterRuleCall(INFAState, ?> state);
/**
* @since 2.0
*/
protected abstract boolean canPass(CTX context, INFAState, ?> state, EClass constructedType);
/**
* @since 2.0
*/
protected EClass getConstructedType(AbstractElement ele) {
if (ele instanceof Action)
return (EClass) ((Action) ele).getType().getClassifier();
if (GrammarUtil.containingAssignment(ele) != null)
return (EClass) GrammarUtil.containingRule(ele).getType().getClassifier();
return null;
}
protected boolean canReachContextEnd(PDAContext context, RuleCallStackElement stack, INFAState, ?> fromNfa,
EClass constructedType, boolean returning, boolean canReturn, Set>> visited) {
if (stack == null || !visited.add(Tuples.> create(returning, fromNfa)))
return false;
if (!canPass(context.context, fromNfa, constructedType))
return false;
else if (constructedType == null)
constructedType = getConstructedType(fromNfa.getGrammarElement());
if (isFinalState(context.context, fromNfa, returning, canReturn))
return true;
if (!returning && canEnterRuleCall(fromNfa)) {
if (stack.hasEnteredTwice(fromNfa))
return false;
stack = stackPush(stack, fromNfa);
visited = Sets.newHashSet();
}
for (INFAState, ?> follower : getFollowers(context.context, fromNfa, returning, canReturn)) {
boolean targetCanReturn = !canEnterRuleCall(follower);
boolean targetReturning = follower.hasOutgoingRuleCall() && targetCanReturn;
if (canReachContextEnd(context, stack, follower, constructedType, targetReturning, targetCanReturn, visited))
return true;
}
if (canReturn && fromNfa.isEndState() && stack != null && stack.getRuleCall() != null) {
visited = Sets.newHashSet();
if (canReachContextEnd(context, stack.getParent(), stack.getRuleCall(), constructedType, true, true,
visited))
return true;
}
return false;
}
/**
* @since 2.0
*/
protected PDAContext createContext(CTX obj) {
return new PDAContext(obj);
}
protected PDAState createState(IPDAState.PDAStateType type, AbstractElement element) {
return new PDAState(type, element);
}
/**
* @since 2.0
*/
protected PDAState createState(PDAContext ctx, RuleCallStackElement stack, INFAState, ?> fromNfa,
EClass constructedType, boolean returning, boolean canReturn, Set>> visited) {
Set>> visited2 = Sets.newHashSet();
if (stack == null
|| !canReachContextEnd(ctx, stack.cloneWithoutVisited(), fromNfa, constructedType, returning,
canReturn, visited2))
return null;
if (constructedType == null)
constructedType = getConstructedType(fromNfa.getGrammarElement());
AbstractElement ge = fromNfa.getGrammarElement();
PDAState result = null;
if (canEnterRuleCall(fromNfa)) {
if (returning) {
if ((result = ctx.ruleCallExit.get(fromNfa)) == null)
ctx.ruleCallExit.put(fromNfa, result = createState(PDAStateType.RULECALL_EXIT, ge));
} else {
if ((result = ctx.ruleCallEnter.get(fromNfa)) == null)
ctx.ruleCallEnter.put(fromNfa, result = createState(PDAStateType.RULECALL_ENTER, ge));
}
} else {
if ((result = ctx.elements.get(fromNfa)) == null)
ctx.elements.put(fromNfa, result = createState(PDAStateType.ELEMENT, ge));
}
if (!visited.add(Tuples.> create(returning, fromNfa)))
return result;
if (!returning && canEnterRuleCall(fromNfa)) {
if (stack.hasEnteredTwice(fromNfa))
return result;
stack = stackPush(stack, fromNfa);
visited = Sets.newHashSet();
}
if (result.followers == null || result.followers == Collections.EMPTY_SET) {
result.followers = Sets.newHashSet();
}
if (isFinalState(ctx.context, fromNfa, returning, canReturn))
result.followers.add(ctx.stop);
for (INFAState, ?> follower : getFollowers(ctx.context, fromNfa, returning, canReturn)) {
boolean folCanReturn = !canEnterRuleCall(follower);
boolean folReturning = follower.hasOutgoingRuleCall() && folCanReturn;
PDAState r = createState(ctx, stack, follower, constructedType, folReturning, folCanReturn, visited);
if (r != null)
result.followers.add(r);
}
if (canReturn && fromNfa.isEndState() && stack != null && stack.getRuleCall() != null) {
visited = Sets.newHashSet();
PDAState r = createState(ctx, stack.getParent(), stack.getRuleCall(), constructedType, true, true, visited);
if (r != null)
result.followers.add(r);
}
return result;
}
/**
* @since 2.0
*/
public IPDAState getPDA(CTX context) {
PDAContext ctx = createContext(context);
ctx.start = createState(IPDAState.PDAStateType.START, null);
ctx.stop = createState(IPDAState.PDAStateType.STOP, null);
Set>> visited = Sets.newHashSet();
ctx.start.followers = Sets.newHashSet();
for (INFAState, ?> state : getStartFollowers(context)) {
boolean targetCanReturn = !canEnterRuleCall(state);
boolean targetReturning = !state.getOutgoingAfterReturn().isEmpty() && targetCanReturn;
RuleCallStackElement stack = new RuleCallStackElement(null, null);
IPDAState s = createState(ctx, stack, state, null, targetReturning, targetCanReturn, visited);
if (s != null)
ctx.start.followers.add(s);
}
return ctx.start;
}
/**
* @since 2.0
*/
protected abstract List> getStartFollowers(CTX context);
protected PDAState getState(PDAContext ctx, INFAState, ?> state, boolean returning) {
PDAState result = ctx.elements.get(state);
if (result == null) {
if (returning)
result = ctx.ruleCallExit.get(state);
else
result = ctx.ruleCallEnter.get(state);
}
return result;
}
protected abstract boolean isFinalState(CTX context, INFAState, ?> state, boolean returning, boolean canReturn);
/**
* @since 2.0
*/
protected abstract List> getFollowers(CTX context, INFAState, ?> state, boolean returning,
boolean canReturn);
protected RuleCallStackElement stackPush(RuleCallStackElement stack, INFAState, ?> value) {
return new RuleCallStackElement(stack, value);
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy