org.eclipse.xtext.serializer.analysis.SyntacticSequencerPDAProvider 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.serializer.analysis;
import java.util.Collections;
import java.util.EnumSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.eclipse.emf.ecore.EClass;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.xtext.AbstractElement;
import org.eclipse.xtext.Action;
import org.eclipse.xtext.Assignment;
import org.eclipse.xtext.EnumRule;
import org.eclipse.xtext.GrammarUtil;
import org.eclipse.xtext.Keyword;
import org.eclipse.xtext.ParserRule;
import org.eclipse.xtext.RuleCall;
import org.eclipse.xtext.TerminalRule;
import org.eclipse.xtext.grammaranalysis.impl.GrammarElementTitleSwitch;
import org.eclipse.xtext.serializer.analysis.GrammarAlias.AbstractElementAlias;
import org.eclipse.xtext.serializer.analysis.GrammarAlias.AlternativeAlias;
import org.eclipse.xtext.serializer.analysis.GrammarAlias.GrammarAliasFactory;
import org.eclipse.xtext.serializer.analysis.GrammarAlias.GroupAlias;
import org.eclipse.xtext.serializer.analysis.GrammarAlias.TokenAlias;
import org.eclipse.xtext.serializer.analysis.ISerState.SerStateType;
import org.eclipse.xtext.serializer.sequencer.RuleCallStack;
import org.eclipse.xtext.util.Pair;
import org.eclipse.xtext.util.Tuples;
import org.eclipse.xtext.util.formallang.Nfa;
import org.eclipse.xtext.util.formallang.NfaToProduction;
import org.eclipse.xtext.util.formallang.NfaUtil;
import org.eclipse.xtext.util.formallang.Pda;
import org.eclipse.xtext.util.formallang.PdaUtil;
import com.google.common.base.Function;
import com.google.common.base.Predicate;
import com.google.common.base.Predicates;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import com.google.inject.Inject;
import com.google.inject.Singleton;
/**
* @author Moritz Eysholdt - Initial contribution and API
*/
@Singleton
public class SyntacticSequencerPDAProvider implements ISyntacticSequencerPDAProvider {
protected static class NavigablePDA implements Pda {
protected ISynNavigable navigable;
public NavigablePDA(ISynNavigable navigable) {
super();
this.navigable = navigable;
}
public ISynState getStop() {
return navigable.getTarget();
}
public Iterable getFollowers(ISynState node) {
if (node instanceof ISynAbsorberState) {
if (navigable instanceof ISynTransition && ((ISynTransition) navigable).getSource() == node)
return navigable.getFollowers();
return Collections.emptyList();
}
return node.getFollowers();
}
public RuleCall getPop(ISynState state) {
return state.getType() == SynStateType.UNASSIGNED_PARSER_RULE_EXIT ? (RuleCall) state.getGrammarElement()
: null;
}
public RuleCall getPush(ISynState state) {
return state.getType() == SynStateType.UNASSIGNED_PARSER_RULE_ENTER ? (RuleCall) state.getGrammarElement()
: null;
}
public ISynState getStart() {
if (navigable instanceof ISynTransition)
return ((ISynTransition) navigable).getSource();
return (ISynState) navigable;
}
}
protected static class SynAbsorberState extends SynState implements ISynAbsorberState {
protected EObject context;
protected EClass eClass;
protected Map outTransitionsByElement = Maps.newHashMap();
protected List outAbsorber = Lists.newArrayList();
public SynAbsorberState(SynStateType type, AbstractElement element, EObject context, EClass eClass) {
super(type, element);
this.context = context;
this.eClass = eClass;
}
protected void addTransition(ISynTransition transition) {
addFollower(transition.getFollowers());
outAbsorber.add(transition.getTarget());
switch (transition.getTarget().getType().getSimpleType()) {
case START:
throw new UnsupportedOperationException("StartStates can not have incoming transitions");
case ELEMENT:
case STOP:
if (outTransitionsByElement.isEmpty())
outTransitionsByElement = Maps.newHashMap();
outTransitionsByElement.put(transition.getTarget().getGrammarElement(), transition);
break;
case RULECALL_ENTER:
case RULECALL_EXIT:
}
}
public EObject getContext() {
return context;
}
public EClass getEClass() {
return eClass;
}
public List getOutTransitions() {
List result = Lists.newArrayList();
result.addAll(outTransitionsByElement.values());
return result;
}
public Map getOutTransitionsByElement() {
return outTransitionsByElement;
}
public List getOutAbsorbers() {
return outAbsorber;
}
}
protected static class SynEmitterState extends SynNavigable implements ISynEmitterState {
public SynEmitterState(SynStateType type, AbstractElement element, SynAbsorberState target) {
super(type, element, target);
}
}
protected static class SynNavigable extends SynState implements ISynNavigable {
protected final static List RULE_EXIT_DEPENDENT = Lists.newArrayList();
protected int distanceToAbsorber = -1;
protected Boolean involvesRuleExit;
protected Boolean involvesUnassignedTokenRuleCalls = null;
protected List shortestPathToAbsorber = null;
protected Boolean syntacticallyAmbiguous = null;
protected ISynAbsorberState target;
public SynNavigable(SynStateType type, AbstractElement element, ISynAbsorberState target) {
super(type, element);
this.target = target;
}
public EObject getContext() {
return target.getContext();
}
public EClass getEClass() {
return target.getEClass();
}
public Pda getPathToTarget() {
return new NavigablePDA(this);
}
public List getShortestPathTo(final AbstractElement ele, RuleCallStack stack) {
return shortestPathTo(stack.iterator(), new Predicate() {
public boolean apply(ISynState input) {
return input.getGrammarElement() == ele;
}
}, true);
}
public List getShortestPathToAbsorber(RuleCallStack stack) {
if (involvesRuleExit())
return shortestPathTo(stack.iterator(), Predicates. equalTo(getTarget()), false);
if (shortestPathToAbsorber == null)
shortestPathToAbsorber = shortestPathTo(stack.iterator(), Predicates. equalTo(getTarget()),
false);
return shortestPathToAbsorber;
}
public List getShortestStackpruningPathTo(final AbstractElement ele, RuleCallStack stack) {
return shortestStackpruningPathTo(stack.iterator(), new Predicate() {
public boolean apply(ISynState input) {
return input.getGrammarElement() == ele;
}
}, true);
}
public List getShortestStackpruningPathToAbsorber(RuleCallStack stack) {
if (involvesRuleExit())
return shortestStackpruningPathTo(stack.iterator(), Predicates. equalTo(getTarget()), false);
if (shortestPathToAbsorber == null)
shortestPathToAbsorber = shortestPathTo(stack.iterator(), Predicates. equalTo(getTarget()),
false);
return shortestPathToAbsorber;
}
public ISynAbsorberState getTarget() {
return target;
}
public boolean hasEmitters() {
if (getFollowers().size() == 1 && getFollowers().get(0) instanceof ISynAbsorberState)
return false;
return true;
}
protected boolean involves(ISynState from, Set types, Set visited) {
if (types.contains(from.getType()))
return true;
if (!visited.add(from))
return false;
for (ISynState state : from.getFollowers())
if (!(state instanceof ISynAbsorberState) && involves(state, types, visited))
return true;
return false;
}
protected boolean involves(Set types) {
Set visited = Sets.newHashSet();
for (ISynState state : followers)
if (involves(state, types, visited))
return true;
return false;
}
protected Boolean involvesRuleExit() {
if (involvesRuleExit == null)
involvesRuleExit = involves(EnumSet.of(SynStateType.UNASSIGNED_PARSER_RULE_EXIT));
return involvesRuleExit;
}
public boolean involvesUnassignedTokenRuleCalls() {
if (involvesUnassignedTokenRuleCalls == null)
involvesUnassignedTokenRuleCalls = involves(EnumSet.of(SynStateType.UNASSIGNED_DATATYPE_RULE_CALL,
SynStateType.UNASSIGNED_TERMINAL_RULE_CALL));
return involvesUnassignedTokenRuleCalls;
}
public boolean isSyntacticallyAmbiguous() {
if (syntacticallyAmbiguous == null)
syntacticallyAmbiguous = isSyntacticallyAmbiguous(followers);
return syntacticallyAmbiguous;
}
protected boolean isSyntacticallyAmbiguous(ISynState state, RuleCallStack exits, RuleCallStack stack,
List results, Set visited) {
if (!visited.add(state))
return true;
if (state instanceof ISynAbsorberState) {
results.add(exits);
return false;
}
switch (state.getType().getSimpleType()) {
case RULECALL_ENTER:
stack = stack.cloneAndPush((RuleCall) state.getGrammarElement());
break;
case RULECALL_EXIT:
RuleCall rc = (RuleCall) state.getGrammarElement();
if (!stack.isEmpty()) {
if (rc == stack.peek())
stack = stack.cloneAndPop();
else
return false;
} else {
if (exits.contains(rc))
return false;
else {
visited = Sets.newHashSet();
exits = exits.cloneAndPush(rc);
}
}
break;
default:
break;
}
for (ISynState follower : state.getFollowers())
if (isSyntacticallyAmbiguous(follower, exits, stack, results, visited))
return true;
return false;
}
protected boolean isSyntacticallyAmbiguous(List states) {
RuleCallStack exits = new RuleCallStack();
RuleCallStack stack = new RuleCallStack();
List results = Lists.newArrayList();
Set visited = Sets.newHashSet();
for (ISynState state : states)
if (isSyntacticallyAmbiguous(state, exits, stack, results, visited))
return true;
return results.size() != Sets.newHashSet(results).size();
}
protected List shortestPathTo(Iterator stack, Predicate matches,
boolean includeMatch) {
List pathTo = new PdaUtil().shortestPathTo(getPathToTarget(), stack, matches);
if (pathTo != null)
return pathTo.subList(1, pathTo.size() - (includeMatch ? 0 : 1));
return null;
}
protected List shortestStackpruningPathTo(Iterator stack, Predicate matches,
boolean includeMatch) {
List pathTo = new PdaUtil().shortestStackpruningPathTo(getPathToTarget(), stack, matches);
if (pathTo != null)
return pathTo.subList(1, pathTo.size() - (includeMatch ? 0 : 1));
return null;
}
}
protected abstract static class SynState implements ISynState {
protected AbstractElement element;
protected List followers = Collections.emptyList();
protected SynStateType type;
public SynState(SynStateType type, AbstractElement element) {
super();
this.type = type;
this.element = element;
this.followers = Collections.emptyList();
}
protected void addFollower(ISynState follower) {
if (followers.isEmpty())
followers = Lists.newArrayList();
followers.add(follower);
}
protected void addFollower(List follower) {
if (followers.isEmpty())
followers = Lists.newArrayList();
followers.addAll(follower);
}
public List getFollowers() {
return followers;
}
public AbstractElement getGrammarElement() {
return element;
}
public SynStateType getType() {
return type;
}
protected void setFollowers(List followers) {
this.followers = followers;
}
@Override
public String toString() {
return toString(new GrammarElementTitleSwitch().showAssignments().showQualified());
}
public String toString(Function elementFormatter) {
if (type == null)
return "(type is null)";
switch (type.getSimpleType()) {
case ELEMENT:
return element == null ? "(null)" : elementFormatter.apply(element);
case RULECALL_ENTER:
return ">>" + (element == null ? "(null)" : elementFormatter.apply(element));
case RULECALL_EXIT:
return "<<" + (element == null ? "(null)" : elementFormatter.apply(element));
case START:
return "start";
case STOP:
return "stop";
}
return "";
}
}
protected static class SynTransition extends SynNavigable implements ISynTransition {
private final static class Filter implements Predicate {
public boolean apply(ISynState input) {
AbstractElement ge = input.getGrammarElement();
return ge instanceof Keyword || GrammarUtil.isDatatypeRuleCall(ge) || GrammarUtil.isEnumRuleCall(ge)
|| GrammarUtil.isTerminalRuleCall(ge);
}
}
protected static final AbstractElementAlias UNINITIALIZED = new TokenAlias(false, false, null);
protected AbstractElementAlias ambiguousSyntax = UNINITIALIZED;
protected List ambiguousSyntaxes;
protected ISynAbsorberState source;
public SynTransition(ISynAbsorberState source, ISynAbsorberState target) {
super(SynStateType.TRANSITION, null, target);
this.source = source;
}
public AbstractElementAlias getAmbiguousSyntax() {
if (ambiguousSyntax != UNINITIALIZED)
return ambiguousSyntax;
ambiguousSyntax = getShortSyntax();
if (ambiguousSyntax instanceof GroupAlias) {
GroupAlias group = (GroupAlias) ambiguousSyntax;
List children = group.getChildren();
int start = 0;
while (start < children.size() && children.get(start) instanceof TokenAlias
&& !children.get(start).isMany() && !children.get(start).isOptional())
start++;
int end = children.size() - 1;
while (end >= 0 && children.get(end) instanceof TokenAlias && !children.get(end).isMany()
&& !children.get(end).isOptional())
end--;
if (start <= end) {
ambiguousSyntax = group = new GroupAlias(false, false, children.subList(start, end + 1));
if (group.children.size() == 1)
ambiguousSyntax = group.children.get(0);
} else
ambiguousSyntax = null;
}
return ambiguousSyntax;
}
public List getAmbiguousSyntaxes() {
if (ambiguousSyntaxes != null)
return ambiguousSyntaxes;
if (!isSyntacticallyAmbiguous())
return ambiguousSyntaxes = Collections.emptyList();
ambiguousSyntaxes = Lists.newArrayList();
Nfa nfa = new PdaUtil().filterUnambiguousPaths(getPathToTarget());
nfa = new NfaUtil().filter(nfa, new Filter());
AbstractElementAlias syntax = new NfaToProduction().nfaToGrammar(nfa, new GetGrammarElement(),
new GrammarAliasFactory());
if (syntax instanceof GroupAlias) {
GroupAlias group = (GroupAlias) syntax;
for (AbstractElementAlias child : group.getChildren())
if (child.isMany() || child.isOptional() || child instanceof AlternativeAlias)
ambiguousSyntaxes.add(child);
} else
ambiguousSyntaxes.add(syntax);
return ambiguousSyntaxes;
}
public AbstractElementAlias getShortSyntax() {
Nfa path = new NfaUtil().filter(getPathToTarget(), new Filter());
return new NfaToProduction().nfaToGrammar(path, new GetGrammarElement(), new GrammarAliasFactory());
}
public ISynAbsorberState getSource() {
return source;
}
public AbstractElementAlias getSyntax() {
Pda pathToTarget = getPathToTarget();
return new NfaToProduction().nfaToGrammar(pathToTarget, new GetGrammarElement(), new GrammarAliasFactory());
}
@Override
public String toString() {
return getSyntax().toString();
}
}
protected Map, ISynAbsorberState> cache = Maps.newHashMap();
// protected SequencerPDAProvider pdaProvider = createSequencerPDAProvider();
@Inject
protected ContextTypePDAProvider pdaProvider;
protected boolean canReachAbsorber(ISerState from, ISerState to, Set visited) {
if (isMandatoryAbsorber(from.getGrammarElement()) || !visited.add(from))
return false;
for (ISerState follower : from.getFollowers())
if (follower == to)
return true;
else if (canReachAbsorber(follower, to, visited))
return true;
return false;
}
protected void collectFollowingAbsorberStates(ISerState state, boolean collect, Set visited,
Set absorber) {
if (collect) {
if (!visited.add(state))
return;
if (isMandatoryAbsorber(state.getGrammarElement()) || state.getType() == SerStateType.STOP) {
absorber.add(state);
return;
} else if (isOptionalAbsorber(state.getGrammarElement()))
absorber.add(state);
}
for (ISerState follower : state.getFollowers())
collectFollowingAbsorberStates(follower, true, visited, absorber);
}
protected SynAbsorberState createAbsorberState(ISerState state, Map absorbers,
Map> emitters, EObject context, EClass eClass) {
SynAbsorberState result = absorbers.get(state);
if (result != null)
return result;
if (state.getType() == SerStateType.STOP) {
absorbers.put(state, result = createAbsorberState(SynStateType.STOP, null, context, eClass));
return result;
}
absorbers.put(state, result = createAbsorberState(getType(state), state.getGrammarElement(), context, eClass));
Set followers = Sets.newHashSet();
collectFollowingAbsorberStates(state, false, Sets. newHashSet(), followers);
for (ISerState follower : followers) {
SynAbsorberState target = createAbsorberState(follower, absorbers, emitters, context, eClass);
SynTransition transition = createTransition(result, target);
Map emitter = emitters.get(target);
if (emitter == null)
emitters.put(target, emitter = Maps.newHashMap());
transition.setFollowers(createEmitterStates(state, follower, target, emitter));
result.addTransition(transition);
}
return result;
}
protected SynAbsorberState createAbsorberState(SynStateType type, AbstractElement element, EObject context,
EClass eClass) {
return new SynAbsorberState(type, element, context, eClass);
}
protected SynState createEmitterState(SynStateType type, AbstractElement element, SynAbsorberState target) {
return new SynEmitterState(type, element, target);
}
protected List createEmitterStates(ISerState from, ISerState to, SynAbsorberState target,
Map emitters) {
List result = Lists.newArrayList();
for (ISerState next : from.getFollowers())
if (next == to)
result.add(target);
else if (canReachAbsorber(next, to, Sets. newHashSet())) {
SynState emitter = emitters.get(next);
if (emitter == null) {
emitters.put(next, emitter = createEmitterState(getType(next), next.getGrammarElement(), target));
emitter.setFollowers(createEmitterStates(next, to, target, emitters));
}
result.add(emitter);
}
return result;
}
// protected SequencerNFAProvider createSequenceParserNFAProvider() {
// return new SequencerNFAProvider();
// }
//
// protected SequencerPDAProvider createSequencerPDAProvider() {
// return new SequencerPDAProvider(createSequenceParserNFAProvider());
// }
protected SynTransition createTransition(SynAbsorberState source, SynAbsorberState target) {
return new SynTransition(source, target);
}
public ISynAbsorberState getPDA(EObject context, EClass type) {
// SequencerPDAContext ctx = new SequencerPDAContext(context, type);
Pair key = Tuples.create(context, type);
ISynAbsorberState result = cache.get(key);
if (result == null) {
Map absorbers = Maps.newHashMap();
Map> emitters = Maps.newHashMap();
Pda extends ISerState, RuleCall> pda = pdaProvider.getContextTypePDA(context, type);
result = createAbsorberState(pda.getStart(), absorbers, emitters, context, type);
cache.put(key, result);
}
return result;
}
protected SynStateType getType(ISerState state) {
switch (state.getType()) {
case ELEMENT:
AbstractElement ele = state.getGrammarElement();
Assignment ass;
if (ele instanceof Action) {
if (((Action) ele).getFeature() == null)
return SynStateType.UNASSIGEND_ACTION_CALL;
else
return SynStateType.ASSIGNED_ACTION_CALL;
} else if (GrammarUtil.containingCrossReference(ele) != null) {
if (ele instanceof RuleCall) {
RuleCall rc = (RuleCall) ele;
if (rc.getRule() instanceof ParserRule)
return SynStateType.ASSIGNED_CROSSREF_DATATYPE_RULE_CALL;
if (rc.getRule() instanceof TerminalRule)
return SynStateType.ASSIGNED_CROSSREF_TERMINAL_RULE_CALL;
if (rc.getRule() instanceof EnumRule)
return SynStateType.ASSIGNED_CROSSREF_ENUM_RULE_CALL;
} else if (ele instanceof Keyword)
return SynStateType.ASSIGNED_CROSSREF_KEYWORD;
} else if ((ass = GrammarUtil.containingAssignment(ele)) != null) {
if (ele instanceof RuleCall) {
RuleCall rc = (RuleCall) ele;
if (rc.getRule() instanceof ParserRule) {
if (rc.getRule().getType().getClassifier() instanceof EClass)
return SynStateType.ASSIGNED_PARSER_RULE_CALL;
return SynStateType.ASSIGNED_DATATYPE_RULE_CALL;
}
if (rc.getRule() instanceof TerminalRule)
return SynStateType.ASSIGNED_TERMINAL_RULE_CALL;
if (rc.getRule() instanceof EnumRule)
return SynStateType.ASSIGNED_ENUM_RULE_CALL;
} else if (ele instanceof Keyword) {
if (GrammarUtil.isBooleanAssignment(ass))
return SynStateType.ASSIGNED_BOOLEAN_KEYWORD;
else
return SynStateType.ASSIGNED_KEYWORD;
}
} else {
if (ele instanceof RuleCall) {
RuleCall rc = (RuleCall) ele;
if (rc.getRule() instanceof ParserRule)
return SynStateType.UNASSIGNED_DATATYPE_RULE_CALL;
if (rc.getRule() instanceof TerminalRule)
return SynStateType.UNASSIGNED_TERMINAL_RULE_CALL;
} else if (ele instanceof Keyword)
return SynStateType.UNASSIGEND_KEYWORD;
}
break;
case PUSH:
return SynStateType.UNASSIGNED_PARSER_RULE_ENTER;
case POP:
return SynStateType.UNASSIGNED_PARSER_RULE_EXIT;
case START:
return SynStateType.START;
case STOP:
return SynStateType.STOP;
}
throw new RuntimeException("no type found for " + state);
}
protected boolean isMandatoryAbsorber(AbstractElement ele) {
if (ele == null)
return true;
if (GrammarUtil.isAssigned(ele))
return true;
if (GrammarUtil.isAssignedAction(ele))
return true;
// if (GrammarUtil.isDatatypeRuleCall(ele))
// return true;
return false;
}
protected boolean isOptionalAbsorber(AbstractElement ele) {
return false;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy