added support for grammar fragments and refactored pda providers

Signed-off-by: Moritz Eysholdt <moritz.eysholdt@itemis.de>
This commit is contained in:
Moritz Eysholdt 2015-09-28 14:43:27 +02:00
parent 2b95508568
commit fb3e180f8a
23 changed files with 1192 additions and 411 deletions

View file

@ -168,6 +168,10 @@ public class NfaUtil {
return null;
}
public <S, ITERABLE extends Iterable<? extends S>> boolean canReach(Nfa<S> nfa, Predicate<S> matcher) {
return find(nfa, Collections.singleton(nfa.getStart()), matcher) != null;
}
public <S, ITERABLE extends Iterable<? extends S>> boolean canReach(Nfa<S> nfa, S state, Predicate<S> matcher) {
return find(nfa, Collections.singleton(state), matcher) != null;
}
@ -513,5 +517,5 @@ public class NfaUtil {
public <S, COMP extends Comparable<COMP>> Nfa<S> sort(Nfa<S> nfa, Map<S, COMP> comparator) {
return sort(nfa, new MappedComparator<S, COMP>(comparator));
}
}

View file

@ -278,7 +278,8 @@ public class PdaUtil {
public final long UNREACHABLE = Long.MAX_VALUE;
public <S, P> boolean canReach(Pda<S, P> pda, S state, Iterator<P> stack, Predicate<S> matches, Predicate<S> canPass) {
public <S, P> boolean canReach(Pda<S, P> pda, S state, Iterator<P> stack, Predicate<S> matches,
Predicate<S> canPass) {
return distanceTo(pda, Collections.singleton(state), stack, matches, canPass) != UNREACHABLE;
}
@ -594,6 +595,26 @@ public class PdaUtil {
return null;
}
public <S, P, R> List<R> collectReachable(Pda<S, P> pda, final Function<S, R> function) {
final List<R> result = Lists.newArrayList();
Iterator<P> stack = Collections.<P> emptyList().iterator();
Predicate<S> matches = Predicates.<S> alwaysFalse();
Predicate<S> canPass = new Predicate<S>() {
@Override
public boolean apply(S input) {
R r = function.apply(input);
if (r != null) {
result.add(r);
return false;
} else {
return true;
}
}
};
trace(pda, Collections.singleton(pda.getStart()), stack, matches, canPass);
return result;
}
protected <S, P> TraceItem<S, P> trace(Pda<S, P> pda, Iterable<S> starts, Iterator<P> stack, Predicate<S> matches,
Predicate<S> canPass) {
StackItem<P> stackItem = createStack(stack);
@ -693,75 +714,36 @@ public class PdaUtil {
return filterEdges(pda, traverser, factory);
}
// public IPdaAdapter<String, String, String> parse(String pda) {
// Pattern node = Pattern.compile("([a-z-A-Z0-9]*)\\[([a-z-A-Z0-9,= ]*)\\]");
// Pattern transition = Pattern.compile("([a-z-A-Z0-9]*) -> ([a-z-A-Z0-9]*)");
// final Set<String> starts = Sets.newLinkedHashSet();
// final Set<String> finals = Sets.newLinkedHashSet();
// final Multimap<String, String> followers = HashMultimap.create();
// final Map<String, String> pushs = Maps.newLinkedHashMap();
// final Map<String, String> pops = Maps.newLinkedHashMap();
// for (String line : pda.split("\\n")) {
// line = line.trim();
// Matcher nodeMatcher = node.matcher(line);
// if (nodeMatcher.find()) {
// String name = nodeMatcher.group(1);
// String[] args = nodeMatcher.group(2).split(",");
// for (String arg : args) {
// String[] a = arg.split("=");
// String argKey, argValue;
// if (a.length > 1) {
// argKey = a[0];
// argValue = a[1];
// } else {
// argKey = arg;
// argValue = null;
// }
// if ("start".equals(argKey))
// starts.add(name);
// else if ("final".equals(argKey))
// finals.add(name);
// else if ("push".equals(argKey))
// pushs.put(name, argValue);
// else if ("pop".equals(argKey))
// pops.put(name, argValue);
// else
// throw new RuntimeException("unknown state argument key: '" + argKey + "'");
// }
// } else {
// Matcher transitionMatcher = transition.matcher(line);
// if (transitionMatcher.find())
// followers.put(transitionMatcher.group(1), transitionMatcher.group(2));
// else
// throw new RuntimeException("Could not parse line: '" + line + "'");
// }
//
// }
// return new IPdaAdapter<String, String, String>() {
//
// public String getToken(String owner) {
// return null;
// }
//
// public Iterable<String> getStartStates() {
// return starts;
// }
//
// public Iterable<String> getFollowers(String node) {
// return followers.get(node);
// }
//
// public Iterable<String> getFinalStates() {
// return finals;
// }
//
// public String getPush(String state) {
// return pushs.get(state);
// }
//
// public String getPop(String state) {
// return pops.get(state);
// }
// };
// }
public <S, P, D extends Pda<S, P>> Map<P, Pair<S, S>> collectPopsAndPushs(Pda<S, P> pda) {
Map<P, Pair<S, S>> result = Maps.newLinkedHashMap();
for (S s : nfaUtil.collect(pda)) {
P push = pda.getPush(s);
if (push != null) {
Pair<S, S> o = result.get(push);
Pair<S, S> n = Tuples.create(s, o == null ? null : o.getSecond());
result.put(push, n);
}
P pop = pda.getPop(s);
if (pop != null) {
Pair<S, S> o = result.get(pop);
Pair<S, S> n = Tuples.create(o == null ? null : o.getFirst(), s);
result.put(pop, n);
}
}
return result;
}
public <S, P, D extends Pda<S, P>> Map<S, S> mapPopAndPush(Pda<S, P> pda) {
Map<P, Pair<S, S>> popsAndPushs = collectPopsAndPushs(pda);
Map<S, S> result = Maps.newLinkedHashMap();
for (Pair<S, S> p : popsAndPushs.values()) {
S push = p.getFirst();
S pop = p.getSecond();
if (push != null && pop != null) {
result.put(push, pop);
result.put(pop, push);
}
}
return result;
}
}

View file

@ -7,26 +7,34 @@
*******************************************************************************/
package org.eclipse.xtext.serializer.analysis;
import java.util.Collection;
import java.util.HashMap;
import java.util.IdentityHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.xtext.AbstractElement;
import org.eclipse.xtext.Action;
import org.eclipse.xtext.Grammar;
import org.eclipse.xtext.GrammarUtil;
import org.eclipse.xtext.ParserRule;
import org.eclipse.xtext.RuleCall;
import org.eclipse.xtext.grammaranalysis.impl.CfgAdapter;
import org.eclipse.xtext.serializer.analysis.SerializerPDA.SerializerPDACloneFactory;
import org.eclipse.xtext.serializer.analysis.ISerState.SerStateType;
import org.eclipse.xtext.serializer.analysis.SerializerPDA.SerializerPDAElementFactory;
import org.eclipse.xtext.serializer.analysis.SerializerPDA.SerializerPDAGetToken;
import org.eclipse.xtext.util.formallang.FollowerFunctionImpl;
import org.eclipse.xtext.serializer.analysis.SerializerPDA.SerializerPDAState;
import org.eclipse.xtext.util.Pair;
import org.eclipse.xtext.util.Tuples;
import org.eclipse.xtext.util.formallang.NfaUtil;
import org.eclipse.xtext.util.formallang.Pda;
import org.eclipse.xtext.util.formallang.PdaUtil;
import org.eclipse.xtext.util.formallang.Production;
import com.google.common.base.Function;
import com.google.common.collect.HashMultimap;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Multimap;
import com.google.common.collect.Sets;
import com.google.inject.Inject;
import com.google.inject.Singleton;
@ -37,170 +45,221 @@ import com.google.inject.Singleton;
@Singleton
public class ContextPDAProvider implements IContextPDAProvider {
protected class ExpandRuleCalls implements Function<ISerState, Pda<ISerState, RuleCall>> {
@Override
public Pda<ISerState, RuleCall> apply(ISerState input) {
if (GrammarUtil.isUnassignedEObjectRuleCall(input.getGrammarElement()))
return getContextPDA((((RuleCall) input.getGrammarElement()).getRule()));
return null;
protected static class CallStack {
private final RuleCall call;
private final CallStack parent;
private final Set<ISerState> visited = Sets.newHashSet();
public CallStack(CallStack parent, RuleCall call) {
super();
this.parent = parent;
this.call = call;
}
public boolean isLoop() {
CallStack current = parent;
int counter = 0;
while (current != null) {
if (current.call == call)
counter++;
if (counter >= 2)
return true;
current = current.parent;
}
return false;
}
}
protected static class SerializerActionCfg extends CfgAdapter {
protected Action context;
private final Map<Grammar, Map<EObject, Pda<ISerState, RuleCall>>> cache = Maps.newHashMap();
public SerializerActionCfg(Action context) {
super(GrammarUtil.getGrammar(context));
this.context = context;
}
@Inject
private SerializerPDAElementFactory factory;
@Override
public AbstractElement getCall(AbstractElement ele) {
return null;
}
@Inject
private IGrammarPDAProvider grammarPdaProvider;
@Override
public AbstractElement getRoot() {
return GrammarUtil.containingRule(context).getAlternatives();
}
}
protected static class SerializerActionFollowerFunction extends
FollowerFunctionImpl<AbstractElement, AbstractElement> {
protected Action actionCtx;
public SerializerActionFollowerFunction(Production<AbstractElement, AbstractElement> production, Action context) {
super(production);
this.actionCtx = context;
}
@Override
public Iterable<AbstractElement> getFollowers(AbstractElement element) {
Set<AbstractElement> result = Sets.newLinkedHashSet();
for (AbstractElement ele : super.getFollowers(element))
if (ele == null) {
} else if (actionCtx == ele)
result.add(null);
else if (!GrammarUtil.isAssignedAction(ele))
result.add(ele);
return result;
}
@Override
public Iterable<AbstractElement> getStarts(AbstractElement root) {
Set<AbstractElement> result = Sets.newLinkedHashSet();
for (Action act : GrammarUtil.containedActions(root))
if (act.getFeature() != null)
result.add(act);
for (AbstractElement ele : super.getStarts(root))
if (ele == null) {
} else if (actionCtx == ele) {
result.add(null);
} else if (!GrammarUtil.isAssignedAction(ele))
result.add(ele);
return result;
}
}
protected static class SerializerParserRuleCfg extends CfgAdapter {
protected ParserRule context;
public SerializerParserRuleCfg(ParserRule context) {
super(GrammarUtil.getGrammar(context));
this.context = context;
}
@Override
public AbstractElement getCall(AbstractElement ele) {
if (ele instanceof RuleCall && !GrammarUtil.isAssigned(ele)
&& GrammarUtil.isEObjectRuleCall(ele))
return ((RuleCall) ele).getRule().getAlternatives();
return null;
}
@Override
public AbstractElement getRoot() {
return context.getAlternatives();
}
}
protected static class SerializerParserRuleFollowerFunction extends
FollowerFunctionImpl<AbstractElement, AbstractElement> {
protected ParserRule ruleCtx;
public SerializerParserRuleFollowerFunction(Production<AbstractElement, AbstractElement> production,
ParserRule context) {
super(production);
this.ruleCtx = context;
}
@Override
public Iterable<AbstractElement> getFollowers(AbstractElement element) {
Set<AbstractElement> result = Sets.newLinkedHashSet();
for (AbstractElement ele : super.getFollowers(element))
if (ele == null)
result.add(null);
else if (!GrammarUtil.isAssignedAction(ele))
result.add(ele);
return result;
}
@Override
public Iterable<AbstractElement> getStarts(AbstractElement root) {
Set<AbstractElement> result = Sets.newLinkedHashSet();
for (Action act : GrammarUtil.containedActions(root))
if (act.getFeature() != null)
result.add(act);
for (AbstractElement ele : super.getStarts(root))
if (ele == null)
result.add(null);
else if (!GrammarUtil.isAssignedAction(ele))
result.add(ele);
return result;
}
}
protected Map<EObject, Pda<ISerState, RuleCall>> cache = Maps.newHashMap();
@Inject
private NfaUtil nfaUtil;
@Inject
protected PdaUtil pdaUtil;
protected Pda<ISerState, RuleCall> createPDA(Action action) {
SerializerActionCfg cfg = new SerializerActionCfg(action);
SerializerActionFollowerFunction ff = new SerializerActionFollowerFunction(cfg, action);
SerializerPDAElementFactory fact = new SerializerPDAElementFactory();
Pda<ISerState, RuleCall> actionpda = pdaUtil.create(cfg, ff, fact);
SerializerPDAGetToken getToken = new SerializerPDAGetToken();
Pda<ISerState, RuleCall> expandedpda = pdaUtil.expand(actionpda, new ExpandRuleCalls(), getToken, fact);
Pda<ISerState, RuleCall> filteredpda = pdaUtil.filterOrphans(expandedpda, new SerializerPDACloneFactory());
return filteredpda;
protected void collectExtracted(ISerState orig, Collection<? extends ISerState> precedents, SerializerPDAState copy,
Map<Pair<AbstractElement, SerStateType>, SerializerPDAState> oldToNew, final CallStack inTop,
SerializerPDAState start) {
for (ISerState pre : precedents) {
CallStack top = inTop;
AbstractElement element = pre.getGrammarElement();
switch (pre.getType()) {
case START:
if (top.call == null)
connect(start, copy);
continue;
case POP:
top = new CallStack(top, (RuleCall) element);
if (top.isLoop())
continue;
break;
case PUSH:
if (top.call == null) {
connect(start, copy);
continue;
} else if (top.call == element) {
top = top.parent;
} else {
continue;
}
default:
break;
}
Pair<AbstractElement, SerStateType> key = Tuples.create(element, pre.getType());
SerializerPDAState pre2 = oldToNew.get(key);
if (pre2 == null) {
pre2 = new SerializerPDAState(element, pre.getType());
oldToNew.put(key, pre2);
}
if (GrammarUtil.isAssignedAction(pre.getGrammarElement()) /* && pre.getType() != STOP */) {
Set<ISerState> entries = collectPushForAction(pre);
collectExtracted(pre, entries, pre2, oldToNew, top, start);
} else {
if (top.visited.add(pre))
collectExtracted(pre, pre.getPrecedents(), pre2, oldToNew, top, start);
}
connect(pre2, copy);
}
}
protected Pda<ISerState, RuleCall> createPDA(EObject context, Pda<ISerState, RuleCall> result) {
if (context instanceof ParserRule)
return createPDA((ParserRule) context);
else if (context instanceof Action)
return createPDA((Action) context);
throw new IllegalStateException("illegal context");
}
protected Pda<ISerState, RuleCall> createPDA(ParserRule rule) {
SerializerParserRuleCfg cfg = new SerializerParserRuleCfg(rule);
SerializerParserRuleFollowerFunction ff = new SerializerParserRuleFollowerFunction(cfg, rule);
Pda<ISerState, RuleCall> pda = pdaUtil.create(cfg, ff, new SerializerPDAElementFactory());
return pdaUtil.filterOrphans(pda, new SerializerPDACloneFactory());
}
@Override
public Pda<ISerState, RuleCall> getContextPDA(EObject context) {
Pda<ISerState, RuleCall> result = cache.get(context);
if (result == null)
cache.put(context, result = createPDA(context, result));
protected Set<ISerState> collectPushForAction(ISerState action) {
ParserRule rule = GrammarUtil.containingParserRule(action.getGrammarElement());
LinkedHashSet<ISerState> result = Sets.<ISerState> newLinkedHashSet();
collectPushForAction(action, rule, result, Sets.<ISerState> newHashSet());
return result;
}
protected void collectPushForAction(ISerState state, ParserRule rule, Set<ISerState> result,
Set<ISerState> visited) {
if (!visited.add(state))
return;
switch (state.getType()) {
case START:
result.add(state);
return;
case PUSH:
AbstractElement element = state.getGrammarElement();
if (element instanceof RuleCall && ((RuleCall) element).getRule() == rule) {
result.add(state);
return;
}
default:
break;
}
List<? extends ISerState> precedents = state.getPrecedents();
for (ISerState pre : precedents)
collectPushForAction(pre, rule, result, visited);
}
protected void connect(SerializerPDAState precedent, SerializerPDAState follower) {
if (precedent == null || follower == null)
return;
if (follower.getPrecedents().contains(precedent))
return;
follower.getPrecedents().add(precedent);
precedent.getFollowers().add(follower);
}
protected Pda<ISerState, RuleCall> extract(ISerState last) {
SerializerPDA result = factory.create(null, null);
HashMap<Pair<AbstractElement, SerStateType>, SerializerPDAState> oldToNew = Maps.newHashMap();
CallStack callStack = new CallStack(null, null);
collectExtracted(last, last.getPrecedents(), result.getStop(), oldToNew, callStack, result.getStart());
return result;
}
@Override
public Set<EObject> getAllContexts(Grammar grammar) {
Map<EObject, Pda<ISerState, RuleCall>> map = getOrCreate(grammar);
Set<EObject> result = map.keySet();
return result;
}
protected EObject getContext(AbstractElement ele) {
if (ele instanceof RuleCall) {
if (GrammarUtil.isAssignedEObjectRuleCall(ele))
return ((RuleCall) ele).getRule();
} else if (GrammarUtil.isAssignedAction(ele))
return ele;
return null;
}
@Override
public Pda<ISerState, RuleCall> getContextPDA(Grammar grammar, EObject context) {
Map<EObject, Pda<ISerState, RuleCall>> map = getOrCreate(grammar);
Pda<ISerState, RuleCall> pda = map.get(context);
if (pda == null)
throw new IllegalStateException("Invlid context for serialization: " + context);
return pda;
}
protected Map<EObject, Pda<ISerState, RuleCall>> getOrCreate(Grammar grammar) {
Map<EObject, Pda<ISerState, RuleCall>> result = cache.get(grammar);
if (result != null)
return result;
result = Maps.newLinkedHashMap();
Set<ParserRule> allParserRules = grammarPdaProvider.getAllRules(grammar);
Multimap<Action, Pda<ISerState, RuleCall>> actionPdas = HashMultimap.create();
for (ParserRule rule : allParserRules) {
Pda<ISerState, RuleCall> pda = grammarPdaProvider.getGrammarPDA(grammar, rule);
List<ISerState> actions = Lists.newArrayList();
for (ISerState state : nfaUtil.collect(pda)) {
if (GrammarUtil.isAssignedAction(state.getGrammarElement())) {
actions.add(state);
}
}
if (actions.isEmpty()) {
result.put(rule, pda);
} else {
Pda<ISerState, RuleCall> rulePda = extract(pda.getStop());
result.put(rule, rulePda);
for (ISerState action : actions) {
Pda<ISerState, RuleCall> actionPda = extract(action);
actionPdas.put((Action) action.getGrammarElement(), actionPda);
}
}
}
for (Map.Entry<Action, Collection<Pda<ISerState, RuleCall>>> action : actionPdas.asMap().entrySet()) {
Pda<ISerState, RuleCall> merged = merge(action.getValue());
result.put(action.getKey(), merged);
}
cache.put(grammar, result);
return result;
}
protected Pda<ISerState, RuleCall> merge(Collection<Pda<ISerState, RuleCall>> pdas) {
if (pdas.isEmpty())
throw new IllegalStateException();
if (pdas.size() == 1)
return pdas.iterator().next();
SerializerPDA merged = factory.create(null, null);
Map<ISerState, SerializerPDAState> oldToNew = Maps.newHashMap();
for (Pda<ISerState, RuleCall> pda : pdas) {
oldToNew.put(pda.getStop(), merged.getStop());
merge(pda.getStart(), merged.getStart(), oldToNew, new IdentityHashMap<ISerState, Boolean>());
}
return merged;
}
protected void merge(ISerState orig, SerializerPDAState copy, Map<ISerState, SerializerPDAState> oldToNew,
IdentityHashMap<ISerState, Boolean> visited) {
for (ISerState fol : orig.getFollowers()) {
SerializerPDAState folCopy = oldToNew.get(fol);
if (folCopy == null) {
folCopy = new SerializerPDAState(fol.getGrammarElement(), fol.getType());
oldToNew.put(fol, folCopy);
}
connect(copy, folCopy);
if (visited.put(fol, Boolean.TRUE) == null)
merge(fol, folCopy, oldToNew, visited);
}
}
}

View file

@ -12,84 +12,34 @@ 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.AbstractRule;
import org.eclipse.xtext.Action;
import org.eclipse.xtext.Assignment;
import org.eclipse.xtext.Grammar;
import org.eclipse.xtext.GrammarUtil;
import org.eclipse.xtext.ParserRule;
import org.eclipse.xtext.serializer.analysis.TypeFinderNFAProvider.TypeFinderState;
import org.eclipse.xtext.serializer.analysis.TypeFinderNFAProvider.TypeFinderTransition;
import org.eclipse.xtext.util.EmfFormatter;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import com.google.inject.Inject;
import com.google.inject.Singleton;
/**
* @author Moritz Eysholdt - Initial contribution and API
*
* @deprecated see {@link IContextProvider} for documentation about this classes' replacements.
*/
@Singleton
@Deprecated
public class ContextProvider implements IContextProvider {
@Inject
protected TypeFinderNFAProvider nfaProvider2;
private IContextPDAProvider pdaProvider;
protected void collectTypesForContext(TypeFinderState state, Set<EClass> types, boolean allowLocal,
boolean hasAssignment, Set<Object> visited) {
hasAssignment = hasAssignment || state.getGrammarElement() instanceof Assignment || GrammarUtil.isEObjectFragmentRuleCall(state.getGrammarElement());
if (allowLocal) {
if (state.getGrammarElement() instanceof Action) {
types.add((EClass) ((Action) state.getGrammarElement()).getType().getClassifier());
return;
}
}
if (state.isEndState() && !GrammarUtil.isUnassignedEObjectRuleCall(state.getGrammarElement())) {
if (hasAssignment) {
ParserRule rule = (ParserRule) GrammarUtil.containingRule(state.getGrammarElement());
if (!rule.isFragment()) {
types.add((EClass) rule.getType().getClassifier());
}
} else {
types.add(null);
}
}
if (!visited.add(state))
return;
for (TypeFinderTransition t : state.getAllOutgoing())
if (!(t.isRuleCall() && state.getGrammarElement() instanceof Assignment))
collectTypesForContext(t.getTarget(), types, true, hasAssignment, visited);
}
@Inject
IContextTypePDAProvider typeProvider;
@Override
public List<EObject> getAllContexts(Grammar grammar) {
List<EObject> result = Lists.newArrayList();
for (ParserRule pr : GrammarUtil.allParserRules(grammar))
if (GrammarUtil.isEObjectRule(pr)) {
result.add(pr);
for (Action action : GrammarUtil.containedActions(pr))
if (GrammarUtil.isAssignedAction(action))
result.add(action);
}
return result;
return Lists.newArrayList(pdaProvider.getAllContexts(grammar));
}
@Override
public Set<EClass> getTypesForContext(EObject context) {
Set<EClass> result = Sets.newHashSet();
if (context instanceof AbstractElement) {
AbstractElement ele = (AbstractElement) context;
collectTypesForContext(nfaProvider2.getNFA(ele), result, false, false, Sets.newHashSet());
} else if (context instanceof AbstractRule) {
AbstractElement ele = ((AbstractRule) context).getAlternatives();
collectTypesForContext(nfaProvider2.getNFA(ele), result, true, false, Sets.newHashSet());
if (GrammarUtil.isOptionalCardinality(ele))
result.add(null);
} else
throw new RuntimeException("This is not a valid context: " + EmfFormatter.objPath(context));
return result;
public Set<EClass> getTypesForContext(Grammar grammar, EObject context) {
return typeProvider.getTypesForContext(grammar, context);
}
}

View file

@ -7,24 +7,32 @@
*******************************************************************************/
package org.eclipse.xtext.serializer.analysis;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.eclipse.emf.ecore.EClass;
import org.eclipse.emf.ecore.EClassifier;
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.Grammar;
import org.eclipse.xtext.GrammarUtil;
import org.eclipse.xtext.RuleCall;
import org.eclipse.xtext.TypeRef;
import org.eclipse.xtext.serializer.analysis.ISerState.SerStateType;
import org.eclipse.xtext.serializer.analysis.SerializerPDA.SerializerPDACloneFactory;
import org.eclipse.xtext.util.Pair;
import org.eclipse.xtext.util.Tuples;
import org.eclipse.xtext.util.Wrapper;
import org.eclipse.xtext.util.formallang.Pda;
import org.eclipse.xtext.util.formallang.PdaUtil;
import org.eclipse.xtext.util.formallang.Traverser;
import com.google.common.base.Function;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import com.google.inject.Inject;
import com.google.inject.Singleton;
@ -112,7 +120,7 @@ public class ContextTypePDAProvider implements IContextTypePDAProvider {
}
}
protected static class TypeFilter implements Traverser<Pda<ISerState, RuleCall>, ISerState, FilterState> {
protected class TypeFilter implements Traverser<Pda<ISerState, RuleCall>, ISerState, FilterState> {
final protected EClass type;
public TypeFilter(EClass type) {
@ -125,20 +133,11 @@ public class ContextTypePDAProvider implements IContextTypePDAProvider {
switch (state.getType()) {
case ELEMENT:
if (previous.type == null) {
Assignment ass = GrammarUtil.containingAssignment(state.getGrammarElement());
if (ass != null) {
TypeRef returnType = GrammarUtil.containingRule(ass).getType();
EClassifier cls = returnType != null ? returnType.getClassifier() : null;
if (cls == type)
return new FilterState(previous, type, previous.stack, state);
EClass cls = getInstantiatedType(state.getGrammarElement());
if (cls == type)
return new FilterState(previous, type, previous.stack, state);
if (cls != null)
return null;
}
if (state.getGrammarElement() instanceof Action) {
EClassifier cls = ((Action) state.getGrammarElement()).getType().getClassifier();
if (cls == type)
return new FilterState(previous, type, previous.stack, state);
return null;
}
} else if (state.getGrammarElement() instanceof Action)
return null;
return new FilterState(previous, previous.type, previous.stack, state);
@ -167,16 +166,17 @@ public class ContextTypePDAProvider implements IContextTypePDAProvider {
}
protected Map<Pair<EObject, EClass>, Pda<ISerState, RuleCall>> cache = Maps.newHashMap();
@Inject
protected IContextProvider contextProvider;
@Inject
protected IContextPDAProvider pdaProvider;
protected Pda<ISerState, RuleCall> createPDA(EObject context, EClass type) {
Pda<ISerState, RuleCall> contextPda = pdaProvider.getContextPDA(context);
@Inject
protected PdaUtil pdaUtil;
protected Pda<ISerState, RuleCall> createPDA(Grammar grammar, EObject context, EClass type) {
Pda<ISerState, RuleCall> contextPda = pdaProvider.getContextPDA(grammar, context);
Pda<ISerState, RuleCall> contextTypePda = null;
if (contextProvider.getTypesForContext(context).size() > 1) {
if (getTypesForContext(grammar, context).size() > 1) {
TypeFilter typeFilter = newTypeFilter(type);
SerializerPDACloneFactory factory = new SerializerPDACloneFactory();
contextTypePda = new PdaUtil().filterEdges(contextPda, typeFilter, factory);
@ -186,11 +186,46 @@ public class ContextTypePDAProvider implements IContextTypePDAProvider {
}
@Override
public Pda<ISerState, RuleCall> getContextTypePDA(EObject context, EClass type) {
public Pda<ISerState, RuleCall> getContextTypePDA(Grammar grammar, EObject context, EClass type) {
Pair<EObject, EClass> key = Tuples.create(context, type);
Pda<ISerState, RuleCall> result = cache.get(key);
if (result == null)
cache.put(key, result = createPDA(context, type));
cache.put(key, result = createPDA(grammar, context, type));
return result;
}
protected EClass getInstantiatedType(AbstractElement element) {
Assignment ass = GrammarUtil.containingAssignment(element);
TypeRef type = null;
if (ass != null) {
type = GrammarUtil.containingRule(ass).getType();
} else if (element instanceof Action) {
type = ((Action) element).getType();
}
if (type != null) {
EClassifier classifier = type.getClassifier();
if (classifier instanceof EClass && !classifier.eIsProxy()) {
return (EClass) classifier;
}
}
return null;
}
@Override
public Set<EClass> getTypesForContext(Grammar grammar, EObject context) {
Pda<ISerState, RuleCall> contextPda = pdaProvider.getContextPDA(grammar, context);
final Wrapper<Boolean> canReachStop = new Wrapper<Boolean>(false);
List<EClass> list = pdaUtil.collectReachable(contextPda, new Function<ISerState, EClass>() {
@Override
public EClass apply(ISerState input) {
if (input.getType() == SerStateType.STOP)
canReachStop.set(true);
return getInstantiatedType(input.getGrammarElement());
}
});
Set<EClass> result = Sets.newLinkedHashSet(list);
if (canReachStop.get())
result.add(null);
return result;
}

View file

@ -57,6 +57,7 @@ import com.google.inject.Singleton;
* @author Moritz Eysholdt - Initial contribution and API
*/
@Singleton
@SuppressWarnings("deprecation")
public class GrammarConstraintProvider implements IGrammarConstraintProvider {
protected abstract static class AbstractConstraintContext implements IConstraintContext {
@ -1442,7 +1443,7 @@ public class GrammarConstraintProvider implements IGrammarConstraintProvider {
AssignedActionConstraintContext result = new AssignedActionConstraintContext(context,
context2Name.getContextName(grammar, context));
ActionFilterState start = nfaProvider.getNFA(context);
Set<EClass> types = contextProvider.getTypesForContext(context);
Set<EClass> types = contextProvider.getTypesForContext(grammar, context);
for (EClass type : types) {
if (type == null) {
Constraint constraint = new ActionConstraint(context, null, null, this);
@ -1470,7 +1471,7 @@ public class GrammarConstraintProvider implements IGrammarConstraintProvider {
if (result == null) {
result = Lists.newArrayList();
for (ParserRule parserRule : GrammarUtil.allParserRules(context)) {
if (parserRule.getType() != null && parserRule.getType().getClassifier() instanceof EClass) {
if (parserRule.getType() != null && parserRule.getType().getClassifier() instanceof EClass && !parserRule.isFragment()) {
result.add(getConstraints(context, parserRule));
for (Action action : GrammarUtil.containedActions(parserRule))
if (action.getFeature() != null)
@ -1486,7 +1487,7 @@ public class GrammarConstraintProvider implements IGrammarConstraintProvider {
protected IConstraintContext getConstraints(Grammar grammar, ParserRule context) {
ParserRuleConstraintContext result = new ParserRuleConstraintContext(context,
context2Name.getContextName(grammar, context));
Set<EClass> types = contextProvider.getTypesForContext(context);
Set<EClass> types = contextProvider.getTypesForContext(grammar, context);
for (EClass type : types) {
if (type == null) {
Constraint constraint = new RuleConstraint(context, null, null, this);

View file

@ -0,0 +1,105 @@
/*******************************************************************************
* 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.Map;
import java.util.Set;
import org.eclipse.xtext.AbstractElement;
import org.eclipse.xtext.Grammar;
import org.eclipse.xtext.GrammarUtil;
import org.eclipse.xtext.ParserRule;
import org.eclipse.xtext.RuleCall;
import org.eclipse.xtext.grammaranalysis.impl.CfgAdapter;
import org.eclipse.xtext.serializer.analysis.SerializerPDA.SerializerPDAElementFactory;
import org.eclipse.xtext.util.formallang.FollowerFunctionImpl;
import org.eclipse.xtext.util.formallang.Pda;
import org.eclipse.xtext.util.formallang.PdaUtil;
import org.eclipse.xtext.util.formallang.Production;
import com.google.common.base.Preconditions;
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 GrammarPDAProvider implements IGrammarPDAProvider {
protected static class SerializerParserRuleCfg extends CfgAdapter {
private final ParserRule entryRule;
public SerializerParserRuleCfg(Grammar grammar, ParserRule entryRule) {
super(grammar);
this.entryRule = entryRule;
}
@Override
public AbstractElement getCall(AbstractElement ele) {
if (GrammarUtil.isEObjectRuleCall(ele) && !GrammarUtil.isAssigned(ele)) {
return ((RuleCall) ele).getRule().getAlternatives();
}
return null;
}
@Override
public AbstractElement getRoot() {
return entryRule.getAlternatives();
}
}
protected static class SerializerParserRuleFollowerFunction
extends FollowerFunctionImpl<AbstractElement, AbstractElement> {
public SerializerParserRuleFollowerFunction(Production<AbstractElement, AbstractElement> production) {
super(production);
}
}
protected Map<ParserRule, Pda<ISerState, RuleCall>> cache = Maps.newHashMap();
@Inject
protected PdaUtil pdaUtil;
protected Pda<ISerState, RuleCall> createPDA(Grammar grammar, ParserRule entryRule) {
Preconditions.checkArgument(isValidRule(entryRule));
SerializerParserRuleCfg cfg = new SerializerParserRuleCfg(grammar, entryRule);
SerializerParserRuleFollowerFunction ff = new SerializerParserRuleFollowerFunction(cfg);
Pda<ISerState, RuleCall> pda = pdaUtil.create(cfg, ff, new SerializerPDAElementFactory());
// return pdaUtil.filterOrphans(pda, new SerializerPDACloneFactory());
return pda;
}
@Override
public Pda<ISerState, RuleCall> getGrammarPDA(Grammar grammar, ParserRule entryRule) {
Pda<ISerState, RuleCall> result = cache.get(entryRule);
if (result == null)
cache.put(entryRule, result = createPDA(grammar, entryRule));
return result;
}
protected boolean isValidRule(ParserRule rule) {
return GrammarUtil.isEObjectRule(rule) && !rule.isFragment();
}
@Override
public Set<ParserRule> getAllRules(Grammar grammar) {
Set<ParserRule> result = Sets.newLinkedHashSet();
for (ParserRule rule : GrammarUtil.allParserRules(grammar))
if (isValidRule(rule))
result.add(rule);
return result;
}
}

View file

@ -7,7 +7,10 @@
*******************************************************************************/
package org.eclipse.xtext.serializer.analysis;
import java.util.Set;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.xtext.Grammar;
import org.eclipse.xtext.RuleCall;
import org.eclipse.xtext.util.formallang.Pda;
@ -19,5 +22,7 @@ import com.google.inject.ImplementedBy;
@ImplementedBy(ContextPDAProvider.class)
public interface IContextPDAProvider {
Pda<ISerState, RuleCall> getContextPDA(EObject context);
Pda<ISerState, RuleCall> getContextPDA(Grammar grammar, EObject context);
Set<EObject> getAllContexts(Grammar grammar);
}

View file

@ -18,10 +18,21 @@ import com.google.inject.ImplementedBy;
/**
* @author Moritz Eysholdt - Initial contribution and API
* @deprecated see methods of this class for documentation about their replacements
*/
@Deprecated
@ImplementedBy(ContextProvider.class)
public interface IContextProvider {
/**
* @deprecated use {@link IContextPDAProvider#getAllContexts(Grammar)}
*/
@Deprecated
public List<EObject> getAllContexts(Grammar grammar);
public Set<EClass> getTypesForContext(EObject context);
/**
* @deprecated use {@link IContextTypePDAProvider#getTypesForContext(Grammar, EObject)}
*/
@Deprecated
public Set<EClass> getTypesForContext(Grammar grammar, EObject context);
}

View file

@ -7,8 +7,11 @@
*******************************************************************************/
package org.eclipse.xtext.serializer.analysis;
import java.util.Set;
import org.eclipse.emf.ecore.EClass;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.xtext.Grammar;
import org.eclipse.xtext.RuleCall;
import org.eclipse.xtext.util.formallang.Pda;
@ -20,5 +23,7 @@ import com.google.inject.ImplementedBy;
@ImplementedBy(ContextTypePDAProvider.class)
public interface IContextTypePDAProvider {
Pda<ISerState, RuleCall> getContextTypePDA(EObject context, EClass type);
Pda<ISerState, RuleCall> getContextTypePDA(Grammar grammar, EObject context, EClass type);
public Set<EClass> getTypesForContext(Grammar grammar, EObject context);
}

View file

@ -0,0 +1,28 @@
/*******************************************************************************
* 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.Set;
import org.eclipse.xtext.Grammar;
import org.eclipse.xtext.ParserRule;
import org.eclipse.xtext.RuleCall;
import org.eclipse.xtext.util.formallang.Pda;
import com.google.inject.ImplementedBy;
/**
* @author Moritz Eysholdt - Initial contribution and API
*/
@ImplementedBy(GrammarPDAProvider.class)
public interface IGrammarPDAProvider {
Pda<ISerState, RuleCall> getGrammarPDA(Grammar grammar, ParserRule entryRule);
Set<ParserRule> getAllRules(Grammar grammar);
}

View file

@ -16,8 +16,12 @@ public interface ISerState {
ELEMENT, POP, PUSH, START, STOP;
}
// ISerState getOpposite();
List<? extends ISerState> getFollowers();
List<? extends ISerState> getPrecedents();
AbstractElement getGrammarElement();
SerStateType getType();

View file

@ -7,10 +7,10 @@
*******************************************************************************/
package org.eclipse.xtext.serializer.analysis;
import java.util.Collections;
import java.util.List;
import org.eclipse.xtext.AbstractElement;
import org.eclipse.xtext.GrammarUtil;
import org.eclipse.xtext.RuleCall;
import org.eclipse.xtext.grammaranalysis.impl.GrammarElementTitleSwitch;
import org.eclipse.xtext.serializer.analysis.ISerState.SerStateType;
@ -20,6 +20,7 @@ import org.eclipse.xtext.util.formallang.Pda;
import org.eclipse.xtext.util.formallang.PdaFactory;
import com.google.common.base.Function;
import com.google.common.base.Predicate;
import com.google.common.collect.Lists;
public class SerializerPDA implements Pda<ISerState, RuleCall> {
@ -54,8 +55,8 @@ public class SerializerPDA implements Pda<ISerState, RuleCall> {
}
}
public static class SerializerPDAElementFactory implements
PdaFactory<SerializerPDA, ISerState, RuleCall, AbstractElement> {
public static class SerializerPDAElementFactory
implements PdaFactory<SerializerPDA, ISerState, RuleCall, AbstractElement> {
@Override
public SerializerPDA create(AbstractElement start, AbstractElement stop) {
@ -82,6 +83,9 @@ public class SerializerPDA implements Pda<ISerState, RuleCall> {
@Override
public void setFollowers(SerializerPDA nfa, ISerState owner, Iterable<ISerState> followers) {
((SerializerPDA.SerializerPDAState) owner).followers = Lists.newArrayList(followers);
for (ISerState follower : followers) {
((SerializerPDA.SerializerPDAState) follower).precedents.add(owner);
}
}
}
@ -94,10 +98,21 @@ public class SerializerPDA implements Pda<ISerState, RuleCall> {
}
public static class SerializerPDAIsAssignedAction implements Predicate<ISerState> {
@Override
public boolean apply(ISerState input) {
return input != null && GrammarUtil.isAssignedAction(input.getGrammarElement());
}
}
protected static class SerializerPDAState implements ISerState {
protected List<ISerState> followers = Collections.emptyList();
protected AbstractElement grammarElement;
protected SerStateType type;
protected List<ISerState> followers = Lists.newArrayList();
protected final List<ISerState> precedents = Lists.newArrayList();
protected final AbstractElement grammarElement;
protected final SerStateType type;
// protected final SerializerPDAState opposite;
public SerializerPDAState(AbstractElement grammarElement, SerStateType type) {
super();
@ -151,6 +166,16 @@ public class SerializerPDA implements Pda<ISerState, RuleCall> {
}
return "";
}
@Override
public List<ISerState> getPrecedents() {
return precedents;
}
// @Override
// public ISerState getOpposite() {
// return null;
// }
}
protected SerializerPDA.SerializerPDAState start;
@ -187,12 +212,12 @@ public class SerializerPDA implements Pda<ISerState, RuleCall> {
}
@Override
public ISerState getStart() {
public SerializerPDAState getStart() {
return start;
}
@Override
public ISerState getStop() {
public SerializerPDAState getStop() {
return stop;
}

View file

@ -20,6 +20,7 @@ import org.eclipse.xtext.AbstractElement;
import org.eclipse.xtext.Action;
import org.eclipse.xtext.Assignment;
import org.eclipse.xtext.EnumRule;
import org.eclipse.xtext.Grammar;
import org.eclipse.xtext.GrammarUtil;
import org.eclipse.xtext.Keyword;
import org.eclipse.xtext.ParserRule;
@ -612,7 +613,8 @@ public class SyntacticSequencerPDAProvider implements ISyntacticSequencerPDAProv
if (result == null) {
Map<ISerState, SynAbsorberState> absorbers = Maps.newHashMap();
Map<SynAbsorberState, Map<ISerState, SynState>> emitters = Maps.newHashMap();
Pda<? extends ISerState, RuleCall> pda = pdaProvider.getContextTypePDA(context, type);
Grammar grammar = GrammarUtil.getGrammar(context);
Pda<? extends ISerState, RuleCall> pda = pdaProvider.getContextTypePDA(grammar, context, type);
result = createAbsorberState(pda.getStart(), absorbers, emitters, context, type);
cache.put(key, result);
}

View file

@ -561,5 +561,11 @@ public abstract class AbstractSemanticSequencerTest extends AbstractXtextTests {
EObject model = testSequence("#32 null");
Assert.assertNull(((NullCrossRef) model).getRef());
}
@Test
public void testFragment() throws Exception {
testSequence("#33 foo bar baz");
}
}
}

View file

@ -20,13 +20,11 @@ import org.eclipse.xtext.grammaranalysis.impl.GrammarElementTitleSwitch;
import org.eclipse.xtext.junit4.AbstractXtextTests;
import org.eclipse.xtext.serializer.analysis.Context2NameFunction;
import org.eclipse.xtext.serializer.analysis.IContextPDAProvider;
import org.eclipse.xtext.serializer.analysis.IContextProvider;
import org.eclipse.xtext.serializer.analysis.ISerState;
import org.eclipse.xtext.util.Pair;
import org.eclipse.xtext.util.Tuples;
import org.eclipse.xtext.util.formallang.Pda;
import org.eclipse.xtext.util.formallang.PdaListFormatter;
import org.junit.Ignore;
import org.junit.Test;
import com.google.common.base.Function;
@ -60,7 +58,7 @@ public class ContextPDAProviderTest extends AbstractXtextTests {
private List<Pair<EObject, String>> getContexts(Grammar grammar) {
final Context2NameFunction ctx2name = get(Context2NameFunction.class);
final IContextProvider contextProvider = get(IContextProvider.class);
final IContextPDAProvider contextProvider = get(IContextPDAProvider.class);
List<Pair<EObject, String>> result = Lists.newArrayList();
for (EObject ctx : contextProvider.getAllContexts(grammar))
result.add(Tuples.create(ctx, ctx2name.getContextName(grammar, ctx)));
@ -81,13 +79,15 @@ public class ContextPDAProviderTest extends AbstractXtextTests {
formatter.setStackitemFormatter(new GrammarElementTitleSwitch().showAssignments().hideCardinality());
formatter.sortFollowers();
IContextPDAProvider pdaProvider = get(IContextPDAProvider.class);
for (Pair<EObject, String> ctx : getContexts(grammar)) {
List<Pair<EObject, String>> contexts = getContexts(grammar);
for (Pair<EObject, String> ctx : contexts) {
result.add(ctx.getSecond() + ":");
Pda<ISerState, RuleCall> pda = pdaProvider.getContextPDA(ctx.getFirst());
Pda<ISerState, RuleCall> pda = pdaProvider.getContextPDA(grammar, ctx.getFirst());
result.add(" " + formatter.format(pda).replace("\n", "\n "));
// StackTraceElement ele = Thread.currentThread().getStackTrace()[2];
// String name = getClass().getSimpleName() + "_" + ele.getMethodName() + "_" + ctx.getSecond() + ".pdf";
// new PdaToDot<ISerState, RuleCall>().draw(pda, name, "-T pdf");
// StackTraceElement ele = Thread.currentThread().getStackTrace()[2];
// String name = getClass().getSimpleName() + "_" + ele.getMethodName() + "_" + ctx.getSecond() + ".pdf";
// new PdaToDot<ISerState, RuleCall>().draw(pda, "dot/" + name, "-T pdf");
}
return Joiner.on("\n").join(result);
}
@ -97,61 +97,83 @@ public class ContextPDAProviderTest extends AbstractXtextTests {
super.setUp();
with(XtextStandaloneSetup.class);
}
@Test
@Ignore("TODO implement me, expectation is not correct so far")
public void testFragmentWithAction() throws Exception {
String actual = getParserRule("R: f1=ID F; fragment F returns R: {A.prev=current} f2=ID;");
String actual = getParserRule("R: 'kw1' F; fragment F returns R: f1=ID {A.prev=current} f2=ID;");
StringBuilder expected = new StringBuilder();
expected.append("Rule:\n");
expected.append(" start -> {Act.val2=}\n");
expected.append(" val3=ID -> stop\n");
expected.append(" {Act.val2=} -> val3=ID\n");
expected.append("Rule_Act_1:\n");
expected.append(" start -> val1=ID\n");
expected.append(" val1=ID -> stop");
expected.append("F_A_1:\n");
expected.append(" start -> f1=ID\n");
expected.append(" f1=ID -> stop\n");
expected.append("R:\n");
expected.append(" start -> 'kw1'\n");
expected.append(" 'kw1' -> >>F\n");
expected.append(" <<F -> stop\n");
expected.append(" >>F -> {A.prev=}\n");
expected.append(" f2=ID -> <<F\n");
expected.append(" {A.prev=} -> f2=ID");
assertEquals(expected.toString(), actual);
}
@Test
@Ignore("TODO implement me, expectation is not correct so far")
public void testFragmentWithActionRecursive() throws Exception {
String actual = getParserRule("R: f1=ID F; fragment F returns R: {A.prev=current} f2=ID F?;");
String actual = getParserRule("R: 'kw1' F; fragment F returns R: 'kw2' F? f1=ID {A.prev=current} f2=ID;");
StringBuilder expected = new StringBuilder();
expected.append("Rule:\n");
expected.append(" start -> a1=ID\n");
expected.append(" 'kw1' -> a2=ID\n");
expected.append(" 'kw2' -> a2=ID\n");
expected.append(" a1=ID -> 'kw1', 'kw2'\n");
expected.append(" a2=ID -> stop");
expected.append("F_A_3:\n");
expected.append(" start -> 'kw2'\n");
expected.append(" 'kw2' -> >>F, f1=ID\n");
expected.append(" <<F -> f1=ID\n");
expected.append(" >>F -> {A.prev=}\n");
expected.append(" f1=ID -> stop\n");
expected.append(" f2=ID -> <<F\n");
expected.append(" {A.prev=} -> f2=ID\n");
expected.append("R:\n");
expected.append(" start -> 'kw1'\n");
expected.append(" 'kw1' -> >>F\n");
expected.append(" <<F -> stop\n");
expected.append(" >>F -> {A.prev=}\n");
expected.append(" f2=ID -> <<F\n");
expected.append(" {A.prev=} -> f2=ID");
assertEquals(expected.toString(), actual);
}
@Test
@Ignore("TODO implement me, expectation is not correct so far")
public void testFragmentWithActionTwice() throws Exception {
String actual = getParserRule("R: f1=ID F; fragment F returns R: {A.prev=current} f2=ID {A.prev=current} f2=ID;");
String g = "R: 'kw1' F; fragment F returns R: f1=ID {A.prev=current} f2=ID {B.prev=current} f3=ID;";
String actual = getParserRule(g);
StringBuilder expected = new StringBuilder();
expected.append("Rule:\n");
expected.append(" start -> a1=ID\n");
expected.append(" 'kw1' -> a2=ID\n");
expected.append(" 'kw2' -> a2=ID\n");
expected.append(" a1=ID -> 'kw1', 'kw2'\n");
expected.append(" a2=ID -> stop");
expected.append("F_A_1:\n");
expected.append(" start -> f1=ID\n");
expected.append(" f1=ID -> stop\n");
expected.append("F_B_3:\n");
expected.append(" start -> {A.prev=}\n");
expected.append(" f2=ID -> stop\n");
expected.append(" {A.prev=} -> f2=ID\n");
expected.append("R:\n");
expected.append(" start -> 'kw1'\n");
expected.append(" 'kw1' -> >>F\n");
expected.append(" <<F -> stop\n");
expected.append(" >>F -> {B.prev=}\n");
expected.append(" f3=ID -> <<F\n");
expected.append(" {B.prev=} -> f3=ID");
assertEquals(expected.toString(), actual);
}
@Test
@Ignore("TODO implement me, expectation is not correct so far")
public void testFragmentWithActionLoop() throws Exception {
String actual = getParserRule("R: f1=ID F; fragment F returns R: ({A.prev=current} f2=ID)*;");
StringBuilder expected = new StringBuilder();
expected.append("Rule:\n");
expected.append(" start -> a1=ID\n");
expected.append(" 'kw1' -> a2=ID\n");
expected.append(" 'kw2' -> a2=ID\n");
expected.append(" a1=ID -> 'kw1', 'kw2'\n");
expected.append(" a2=ID -> stop");
expected.append("F_A_0:\n");
expected.append(" start -> stop, {A.prev=}\n");
expected.append(" f2=ID -> stop\n");
expected.append(" {A.prev=} -> f2=ID\n");
expected.append("R:\n");
expected.append(" start -> f1=ID\n");
expected.append(" <<F -> stop\n");
expected.append(" >>F -> <<F, {A.prev=}\n");
expected.append(" f1=ID -> >>F\n");
expected.append(" f2=ID -> <<F\n");
expected.append(" {A.prev=} -> f2=ID");
assertEquals(expected.toString(), actual);
}
@ -168,6 +190,34 @@ public class ContextPDAProviderTest extends AbstractXtextTests {
assertEquals(expected.toString(), actual);
}
@Test
public void testAssignedEObjectRuleCall() throws Exception {
String actual = getParserRule("Rule: foo=Sub; Sub: id='id';");
StringBuilder expected = new StringBuilder();
expected.append("Rule:\n");
expected.append(" start -> foo=Sub\n");
expected.append(" foo=Sub -> stop\n");
expected.append("Sub:\n");
expected.append(" start -> id='id'\n");
expected.append(" id='id' -> stop");
assertEquals(expected.toString(), actual);
}
@Test
public void testTwoAssignedEObjectRuleCalls() throws Exception {
String actual = getParserRule("Rule: foo1=Sub 'x' foo2=Sub; Sub: id='id';");
StringBuilder expected = new StringBuilder();
expected.append("Rule:\n");
expected.append(" start -> foo1=Sub\n");
expected.append(" 'x' -> foo2=Sub\n");
expected.append(" foo1=Sub -> 'x'\n");
expected.append(" foo2=Sub -> stop\n");
expected.append("Sub:\n");
expected.append(" start -> id='id'\n");
expected.append(" id='id' -> stop");
assertEquals(expected.toString(), actual);
}
@Test
public void testDelegation1() throws Exception {
String actual = getParserRule("Rule: Delegate; Delegate: val=ID;");
@ -185,7 +235,8 @@ public class ContextPDAProviderTest extends AbstractXtextTests {
@Test
public void testDelegation2() throws Exception {
String actual = getParserRule("Rule: Foo | Delegate1; Delegate1: 'del' Delegate2 bar=ID; Delegate2: val=ID; Foo: val2=ID;");
String actual = getParserRule(
"Rule: Foo | Delegate1; Delegate1: 'del' Delegate2 bar=ID; Delegate2: val=ID; Foo: val2=ID;");
StringBuilder expected = new StringBuilder();
expected.append("Delegate1:\n");
expected.append(" start -> 'del'\n");
@ -277,6 +328,35 @@ public class ContextPDAProviderTest extends AbstractXtextTests {
assertEquals(expected.toString(), actual);
}
@Test
public void testDualDelegation() throws Exception {
StringBuilder grammar = new StringBuilder();
grammar.append("Model: foo=AbstractRule;\n");
grammar.append("AbstractRule: Rule1 | Rule2;\n");
grammar.append("Rule1: foo1=ID;\n");
grammar.append("Rule2: foo2=ID;\n");
String actual = getParserRule(grammar.toString());
StringBuilder expected = new StringBuilder();
expected.append("AbstractRule:\n");
expected.append(" start -> >>Rule1, >>Rule2\n");
expected.append(" <<Rule1 -> stop\n");
expected.append(" <<Rule2 -> stop\n");
expected.append(" >>Rule1 -> foo1=ID\n");
expected.append(" >>Rule2 -> foo2=ID\n");
expected.append(" foo1=ID -> <<Rule1\n");
expected.append(" foo2=ID -> <<Rule2\n");
expected.append("Model:\n");
expected.append(" start -> foo=AbstractRule\n");
expected.append(" foo=AbstractRule -> stop\n");
expected.append("Rule1:\n");
expected.append(" start -> foo1=ID\n");
expected.append(" foo1=ID -> stop\n");
expected.append("Rule2:\n");
expected.append(" start -> foo2=ID\n");
expected.append(" foo2=ID -> stop");
assertEquals(expected.toString(), actual);
}
@Test
public void testOptionalEnd() throws Exception {
StringBuilder grammar = new StringBuilder();
@ -358,7 +438,8 @@ public class ContextPDAProviderTest extends AbstractXtextTests {
@Test
public void testExpression1() throws Exception {
String actual = getParserRule("Exp: 'kw' Addit; Addit returns Exp: Prim ({Add.left=current} '+' right=Prim)*; Prim returns Exp: {Val} val=ID;");
String actual = getParserRule(
"Exp: 'kw' Addit; Addit returns Exp: Prim ({Add.left=current} '+' right=Prim)*; Prim returns Exp: {Val} val=ID;");
StringBuilder expected = new StringBuilder();
expected.append("Addit:\n");
expected.append(" start -> >>Prim, {Add.left=}\n");

View file

@ -17,7 +17,8 @@ import org.eclipse.xtext.Grammar;
import org.eclipse.xtext.XtextStandaloneSetup;
import org.eclipse.xtext.junit4.AbstractXtextTests;
import org.eclipse.xtext.serializer.analysis.Context2NameFunction;
import org.eclipse.xtext.serializer.analysis.IContextProvider;
import org.eclipse.xtext.serializer.analysis.IContextPDAProvider;
import org.eclipse.xtext.serializer.analysis.IContextTypePDAProvider;
import org.eclipse.xtext.util.Pair;
import org.eclipse.xtext.util.Tuples;
import org.junit.Ignore;
@ -44,12 +45,13 @@ public class ContextProviderTest extends AbstractXtextTests {
protected String getContexts(String body) throws Exception {
Grammar grammar = (Grammar) getModel(HEADER + body);
IContextProvider contextProvider = get(IContextProvider.class);
IContextPDAProvider contextProvider = get(IContextPDAProvider.class);
IContextTypePDAProvider typeProvider = get(IContextTypePDAProvider.class);
Context2NameFunction names = get(Context2NameFunction.class);
List<Pair<String, List<String>>> result = Lists.newArrayList();
for (EObject context : contextProvider.getAllContexts(grammar)) {
List<String> types = Lists.newArrayList();
for (EClass type : contextProvider.getTypesForContext(context))
for (EClass type : typeProvider.getTypesForContext(grammar, context))
types.add(type == null ? "null" : type.getName());
Collections.sort(types);
result.add(Tuples.create(names.getContextName(grammar, context), types));
@ -74,52 +76,50 @@ public class ContextProviderTest extends AbstractXtextTests {
String expected = "Rule returns Rule";
assertEquals(expected, actual);
}
@Test
@Ignore("TODO implement in context provider")
public void testFragmentIsNotAContext() throws Exception {
String actual = getContexts("Rule: foo=ID; fragment Fragment: name=ID WCFragment; fragment WCFragment*: desc=STRING;");
String actual = getContexts(
"Rule: foo=ID; fragment Fragment: name=ID WCFragment; fragment WCFragment*: desc=STRING;");
String expected = "Rule returns Rule";
assertEquals(expected, actual);
}
@Test
@Ignore("TODO implement in context provider")
public void testActionInFragment() throws Exception {
String actual = getContexts("Rule: foo=ID Fragment; fragment Fragment returns AbstractRule: {AnotherRule.prev=current} name=ID;");
String expected = "Fragment_AnotherRule_0 returns Rule\n" +
"Rule returns AnotherRule";
String actual = getContexts(
"Rule: foo=ID Fragment; fragment Fragment returns AbstractRule: {AnotherRule.prev=current} name=ID;");
String expected = "Fragment_AnotherRule_0 returns Rule\n" + "Rule returns AnotherRule";
assertEquals(expected, actual);
}
@Test
@Ignore("TODO implement in context provider")
public void testTwoActionsInFragment() throws Exception {
String actual = getContexts("Rule0: f1=ID Fragment; fragment Fragment returns Rule: {Rule1.prev=current} f2=ID {Rule2.prev=current} f3=ID;");
String expected = "Fragment_Rule1_0 returns Rule0\n" +
"Fragment_Rule2_2 returns Rule1\n" +
"Rule0 returns Rule2";
String actual = getContexts(
"Rule0: f1=ID Fragment; fragment Fragment returns Rule: {Rule1.prev=current} f2=ID {Rule2.prev=current} f3=ID;");
String expected = "Fragment_Rule1_0 returns Rule0\n" + "Fragment_Rule2_2 returns Rule1\n"
+ "Rule0 returns Rule2";
assertEquals(expected, actual);
}
@Test
@Ignore("TODO implement in context provider")
public void testActionsInFragmentTwoCallers() throws Exception {
String actual = getContexts(
"Rule0: f1=ID Fragment; Rule1: f2=ID Fragment; fragment Fragment returns Rule: {Rule2.prev=current} f3=ID;");
String expected = "Fragment_Rule2_0 returns Rule0, Rule1\n" +
"Rule0 returns Rule2\n" +
"Rule1 returns Rule2";
String expected = "Fragment_Rule2_0 returns Rule0, Rule1\n" + "Rule0 returns Rule2\n" + "Rule1 returns Rule2";
assertEquals(expected, actual);
}
@Test
@Ignore("TODO implement in context provider")
public void testActionsInFragmentTwoPrecedingActions() throws Exception {
String actual = getContexts(
"Rule0: ({Rule1} f1=ID | {Rule2} f1=STRING) Fragment?; fragment Fragment returns Rule: {Rule3.prev=current} f3=ID;");
String expected = "Fragment_Rule3_0 returns Rule1, Rule2\n" +
"Rule0 returns Rule1, Rule2, Rule3";
String expected = "Fragment_Rule3_0 returns Rule1, Rule2\n" + "Rule0 returns Rule1, Rule2, Rule3";
assertEquals(expected, actual);
}

View file

@ -20,7 +20,7 @@ import org.eclipse.xtext.XtextStandaloneSetup;
import org.eclipse.xtext.grammaranalysis.impl.GrammarElementTitleSwitch;
import org.eclipse.xtext.junit4.AbstractXtextTests;
import org.eclipse.xtext.serializer.analysis.Context2NameFunction;
import org.eclipse.xtext.serializer.analysis.IContextProvider;
import org.eclipse.xtext.serializer.analysis.IContextPDAProvider;
import org.eclipse.xtext.serializer.analysis.IContextTypePDAProvider;
import org.eclipse.xtext.serializer.analysis.ISerState;
import org.eclipse.xtext.util.Triple;
@ -60,10 +60,11 @@ public class ContextTypePDAProviderTest extends AbstractXtextTests {
private List<Triple<EClass, EObject, String>> getContexts(Grammar grammar) {
final Context2NameFunction ctx2name = get(Context2NameFunction.class);
final IContextProvider contextProvider = get(IContextProvider.class);
final IContextPDAProvider contextProvider = get(IContextPDAProvider.class);
final IContextTypePDAProvider typeProvider = get(IContextTypePDAProvider.class);
List<Triple<EClass, EObject, String>> result = Lists.newArrayList();
for (EObject ctx : contextProvider.getAllContexts(grammar))
for (EClass type : contextProvider.getTypesForContext(ctx))
for (EClass type : typeProvider.getTypesForContext(grammar, ctx))
result.add(Tuples.create(type, ctx, ctx2name.getContextName(grammar, ctx)));
Collections.sort(result, new Comparator<Triple<EClass, EObject, String>>() {
@Override
@ -93,7 +94,7 @@ public class ContextTypePDAProviderTest extends AbstractXtextTests {
String t = ctx.getFirst() == null ? "null" : ctx.getFirst().getName();
// System.out.println(t + "_" + ctx.getThird() + ":");
result.add(t + "_" + ctx.getThird() + ":");
Pda<? extends ISerState, RuleCall> pda = get(IContextTypePDAProvider.class).getContextTypePDA(
Pda<? extends ISerState, RuleCall> pda = get(IContextTypePDAProvider.class).getContextTypePDA(grammar,
ctx.getSecond(), ctx.getFirst());
result.add(" " + formatter.format((Pda<ISerState, RuleCall>) pda).replace("\n", "\n "));
}
@ -371,4 +372,25 @@ public class ContextTypePDAProviderTest extends AbstractXtextTests {
expected.append(" {Model} -> 'kw1'");
assertEquals(expected.toString(), actual);
}
@Test
public void testUnorderedGroup() throws Exception {
StringBuilder grammar = new StringBuilder();
grammar.append("Model: 'model' '{' (greetings1+=ID+ & greetings2+=ID*) '}';");
String actual = getParserRule(grammar.toString());
StringBuilder expected = new StringBuilder();
expected.append("Model_Model:\n");
expected.append(" start -> 'model'\n");
expected.append(" 'model' -> '{'\n");
expected.append(" '{' -> greetings1+=ID, greetings2+=ID\n");
expected.append(" '}' -> stop\n");
expected.append(" greetings1+=ID -> '}', greetings1+=ID, greetings2+=ID\n");
expected.append(" greetings2+=ID -> '}', greetings1+=ID, greetings2+=ID\n");
expected.append("null_Model:\n");
expected.append(" start -> 'model'\n");
expected.append(" 'model' -> '{'\n");
expected.append(" '{' -> '}'\n");
expected.append(" '}' -> stop");
assertEquals(expected.toString(), actual);
}
}

View file

@ -0,0 +1,432 @@
/*******************************************************************************
* Copyright (c) 2015 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
import com.google.inject.Inject
import org.eclipse.xtext.Grammar
import org.eclipse.xtext.RuleCall
import org.eclipse.xtext.grammaranalysis.impl.GrammarElementTitleSwitch
import org.eclipse.xtext.junit4.InjectWith
import org.eclipse.xtext.junit4.XtextRunner
import org.eclipse.xtext.junit4.internal.XtextInjectorProvider
import org.eclipse.xtext.junit4.util.ParseHelper
import org.eclipse.xtext.junit4.validation.ValidationTestHelper
import org.eclipse.xtext.serializer.analysis.IGrammarPDAProvider
import org.eclipse.xtext.serializer.analysis.ISerState
import org.eclipse.xtext.util.formallang.Pda
import org.eclipse.xtext.util.formallang.PdaListFormatter
import org.eclipse.xtext.util.formallang.PdaToDot
import org.junit.Assert
import org.junit.Ignore
import org.junit.Test
import org.junit.runner.RunWith
/**
* @author Moritz Eysholdt - Initial contribution and API
*/
@RunWith(XtextRunner)
@InjectWith(XtextInjectorProvider)
class GrammarPDAProviderTest {
@Inject IGrammarPDAProvider pdaProvider
@Inject ParseHelper<Grammar> parser
@Inject ValidationTestHelper validator
@Test def void testUnassignedAction() {
val actual = '''
Rule: {Action};
'''.toPda
val expected = '''
Rule:
start -> {Action}
{Action} -> stop
'''
Assert.assertEquals(expected, actual)
}
@Test def void testAssignedAction() {
val actual = '''
Rule: {Foo} {Action.feat=current};
'''.toPda
val expected = '''
Rule:
start -> {Foo}
{Action.feat=} -> stop
{Foo} -> {Action.feat=}
'''
Assert.assertEquals(expected, actual)
}
@Test def void testAssignedTerminalRuleCall() {
val actual = '''
Rule: name=ID;
'''.toPda
val expected = '''
Rule:
start -> name=ID
name=ID -> stop
'''
Assert.assertEquals(expected, actual)
}
@Test def void testAssignedEObjectRuleCall() {
val actual = '''
Rule: call=Called;
Called: name=ID;
'''.toPda
val expected = '''
Rule:
start -> call=Called
call=Called -> stop
Called:
start -> name=ID
name=ID -> stop
'''
Assert.assertEquals(expected, actual)
}
@Test def void testAssignedDatatypeRuleCall() {
val actual = '''
Rule: call=Called;
Called: "foo";
'''.toPda
val expected = '''
Rule:
start -> call=Called
call=Called -> stop
'''
Assert.assertEquals(expected, actual)
}
@Test def void testUnassignedCalledAction() {
val actual = '''
Rule: D1 | D2;
D1: 'd1' A;
D2: 'd2' A;
A: f1=ID {A.l=current} f2=ID;
'''.toPda
val expected = '''
Rule:
start -> >>D1, >>D2
'd1' -> >>A
'd2' -> >>A
<<A -> <<D1
<<A -> <<D2
<<D1 -> stop
<<D2 -> stop
>>A -> f1=ID
>>A -> f1=ID
>>D1 -> 'd1'
>>D2 -> 'd2'
f1=ID -> {A.l=}
f2=ID -> <<A, <<A
{A.l=} -> f2=ID
D1:
start -> 'd1'
'd1' -> >>A
<<A -> stop
>>A -> f1=ID
f1=ID -> {A.l=}
f2=ID -> <<A
{A.l=} -> f2=ID
D2:
start -> 'd2'
'd2' -> >>A
<<A -> stop
>>A -> f1=ID
f1=ID -> {A.l=}
f2=ID -> <<A
{A.l=} -> f2=ID
A:
start -> f1=ID
f1=ID -> {A.l=}
f2=ID -> stop
{A.l=} -> f2=ID
'''
Assert.assertEquals(expected, actual)
}
@Test def void testUnassignedFragmentRuleCall() {
val actual = '''
Rule: Called;
fragment Called returns Abstract: name=ID;
'''.toPda
val expected = '''
Rule:
start -> >>Called
<<Called -> stop
>>Called -> name=ID
name=ID -> <<Called
'''
Assert.assertEquals(expected, actual)
}
@Test def void testUnassignedParserRuleCall() {
val actual = '''
Rule: Called;
Called returns Sub: name=ID;
'''.toPda
val expected = '''
Rule:
start -> >>Called
<<Called -> stop
>>Called -> name=ID
name=ID -> <<Called
Called:
start -> name=ID
name=ID -> stop
'''
Assert.assertEquals(expected, actual)
}
@Test def void testUnassignedWildcardFragmentRuleCall() {
val actual = '''
Rule: Called;
fragment Called*: name=ID;
'''.toPda
val expected = '''
Rule:
start -> >>Called
<<Called -> stop
>>Called -> name=ID
name=ID -> <<Called
'''
Assert.assertEquals(expected, actual)
}
@Test def void testGroup() {
val actual = '''
Rule: {Rule} 'a' 'b' 'c';
'''.toPda
val expected = '''
Rule:
start -> {Rule}
'a' -> 'b'
'b' -> 'c'
'c' -> stop
{Rule} -> 'a'
'''
Assert.assertEquals(expected, actual)
}
@Test def void testAlternative() {
val actual = '''
Rule: {Rule} ('a' | 'b' | 'c');
'''.toPda
val expected = '''
Rule:
start -> {Rule}
'a' -> stop
'b' -> stop
'c' -> stop
{Rule} -> 'a', 'b', 'c'
'''
Assert.assertEquals(expected, actual)
}
@Test def void testUnorderedGroup() {
val actual = '''
Rule: {Rule} ('a' & 'b' & 'c');
'''.toPda
val expected = '''
Rule:
start -> {Rule}
'a' -> 'a', 'b', 'c', stop
'b' -> 'a', 'b', 'c', stop
'c' -> 'a', 'b', 'c', stop
{Rule} -> 'a', 'b', 'c'
'''
Assert.assertEquals(expected, actual)
}
@Test def void testUnorderedGroup2() {
val actual = '''
Rule: {Rule} ('a' & 'b'? & 'c'* & 'd'+);
'''.toPda
val expected = '''
Rule:
start -> {Rule}
'a' -> 'a', 'b', 'c', 'd', stop
'b' -> 'a', 'b', 'c', 'd', stop
'c' -> 'a', 'b', 'c', 'd', stop
'd' -> 'a', 'b', 'c', 'd', stop
{Rule} -> 'a', 'b', 'c', 'd'
'''
Assert.assertEquals(expected, actual)
}
@Test def void testTwoAssignedEObjectRuleCalls() {
val actual = '''
Rule: foo1=Sub foo2=Sub; Sub: id='id';
'''.toPda
val expected = '''
Rule:
start -> foo1=Sub
foo1=Sub -> foo2=Sub
foo2=Sub -> stop
Sub:
start -> id='id'
id='id' -> stop
'''
Assert.assertEquals(expected, actual)
}
@Test def void testRecursion() {
val actual = '''
Recursion: val=ID | '(' Recursion ')';
'''.toPda
val expected = '''
Recursion:
start -> '(', val=ID
'(' -> >>Recursion
')' -> <<Recursion, stop
<<Recursion -> ')'
>>Recursion -> '(', val=ID
val=ID -> <<Recursion, stop
'''
Assert.assertEquals(expected, actual)
}
@Test def void testExpression1() {
val actual = '''
Exp: 'kw' Addit; Addit returns Exp: Prim ({Add.left=current} '+' right=Prim)*; Prim returns Exp: {Val} val=ID;
'''.toPda
val expected = '''
Exp:
start -> 'kw'
'+' -> right=Prim
'kw' -> >>Addit
<<Addit -> stop
<<Prim -> <<Addit, {Add.left=}
>>Addit -> >>Prim
>>Prim -> {Val}
right=Prim -> <<Addit, {Add.left=}
val=ID -> <<Prim
{Add.left=} -> '+'
{Val} -> val=ID
Addit:
start -> >>Prim
'+' -> right=Prim
<<Prim -> stop, {Add.left=}
>>Prim -> {Val}
right=Prim -> stop, {Add.left=}
val=ID -> <<Prim
{Add.left=} -> '+'
{Val} -> val=ID
Prim:
start -> {Val}
val=ID -> stop
{Val} -> val=ID
'''
Assert.assertEquals(expected, actual)
}
@Test def void testActionAlternative() {
val actual = '''
Greeting: '(' Greeting ')' {Foo.child=current} | val=ID;
'''.toPda
val expected = '''
Greeting:
start -> '(', val=ID
'(' -> >>Greeting
')' -> {Foo.child=}
<<Greeting -> ')'
>>Greeting -> '(', val=ID
val=ID -> <<Greeting, stop
{Foo.child=} -> <<Greeting, stop
'''
Assert.assertEquals(expected, actual)
}
@Test @Ignore def void testFragmentWithAction() {
val actual = '''
R: f1=ID F; fragment F returns R: {A.prev=current} f2=ID;
'''.toPda
val expected = '''
R:
start -> f1=ID
<<F -> stop
>>F -> {A.prev=}
f1=ID -> >>F
f2=ID -> <<F
{A.prev=} -> f2=ID
'''
Assert.assertEquals(expected, actual)
}
@Test @Ignore def void testFragmentWithAction2() {
val actual = '''
R: 'kw1a' f1=ID 'kw1b' F; fragment F returns R: 'kw2a' {A.prev=current} 'kw2b' f2=ID 'kw2c';
'''.toPda
val expected = '''
R:
start -> f1=ID
<<F -> stop
>>F -> {A.prev=}
f1=ID -> >>F
f2=ID -> <<F
{A.prev=} -> f2=ID
'''
Assert.assertEquals(expected, actual)
}
@Test @Ignore def void testParameter1() {
val actual = '''
M: "kw1" s=S<true> | "kw2" s=S<false>;
S <P>: <P> v1=ID | <!P> v2=ID;
'''.toPda
val expected = '''
Greeting:
start -> '(', val=ID
'(' -> >>Greeting
')' -> {Foo.child=}
<<Greeting -> ')'
>>Greeting -> '(', val=ID
val=ID -> <<Greeting, stop
{Foo.child=} -> <<Greeting, stop
'''
Assert.assertEquals(expected, actual)
}
def private String toPda(CharSequence rulesText) {
val grammar = parser.parse('''
grammar org.eclipse.xtext.serializer.GrammarPDAProviderTestLanguage with org.eclipse.xtext.common.Terminals
generate GrammarPDAProviderTest "http://www.eclipse.org/2010/tmf/xtext/GrammarPDAProviderTestLanguage"
«rulesText»
''')
validator.assertNoErrors(grammar)
val rules = pdaProvider.getAllRules(grammar)
val pdas = rules.toInvertedMap[pdaProvider.getGrammarPDA(grammar, it)]
// pdas.forEach[p1, p2|p2.toDot(p1.name)]
return pdas.entrySet.map [
'''
«key.name»:
«value.toListString»
'''
].join
}
def protected toDot(Pda<ISerState, RuleCall> pda, String name) {
val test = Thread.currentThread.stackTrace.get(6).methodName
new PdaToDot().draw(pda, "dot2/" + test + "_" + name + ".pdf", "-T pdf")
}
def private toListString(Pda<ISerState, RuleCall> pda) {
val ts = new GrammarElementTitleSwitch().showAssignments().hideCardinality().showQualified()
val formatter = new PdaListFormatter<ISerState, RuleCall>();
formatter.setStateFormatter [
switch (type) { case START: "start" case STOP: "stop" default: ts.apply(grammarElement) }
];
formatter.setStackitemFormatter(new GrammarElementTitleSwitch().showAssignments().hideCardinality());
formatter.sortFollowers();
return formatter.format(pda) + "\n"
}
}

View file

@ -21,7 +21,8 @@ Model:
x19=DependentAlternative1 | x20=DependentAlternative2 | x21=Optional | x22=Float |
x23=UnorderedAlternative | x24=UnorderedGroup | x25=UnorderedGroupOptional |
x26=UnorderedGroupBoolean | x27=Complex1 | x28=OptionalDouble |
x29=NullValueGenerated | x30=NullValueInterpreted | x31=NullCrossRefGenerated | x32=NullCrossRefInterpreted;
x29=NullValueGenerated | x30=NullValueInterpreted | x31=NullCrossRefGenerated | x32=NullCrossRefInterpreted |
x33=FragmentCaller;
SimpleGroup:
"#1" val1=ID val2=ID;
@ -169,4 +170,10 @@ NullCrossRefGenerated returns NullCrossRef:
"#31" ref=[ecore::EObject|ID];
NullCrossRefInterpreted returns NullCrossRef:
"#32" ref=[ecore::EObject|ID] foo=ID?;
"#32" ref=[ecore::EObject|ID] foo=ID?;
FragmentCaller returns FragmentCallerType:
"#33" val1=ID Fragment1 val=ID;
fragment Fragment1 returns FragmentCallerType:
fragVal=ID;

View file

@ -23,7 +23,8 @@ import org.eclipse.xtext.grammaranalysis.IPDAState.PDAStateType;
import org.eclipse.xtext.grammaranalysis.impl.GrammarElementTitleSwitch;
import org.eclipse.xtext.junit4.AbstractXtextTests;
import org.eclipse.xtext.serializer.analysis.Context2NameFunction;
import org.eclipse.xtext.serializer.analysis.IContextProvider;
import org.eclipse.xtext.serializer.analysis.IContextPDAProvider;
import org.eclipse.xtext.serializer.analysis.IContextTypePDAProvider;
import org.eclipse.xtext.serializer.analysis.ISyntacticSequencerPDAProvider;
import org.eclipse.xtext.serializer.analysis.ISyntacticSequencerPDAProvider.ISynAbsorberState;
import org.eclipse.xtext.serializer.analysis.ISyntacticSequencerPDAProvider.ISynState;
@ -71,14 +72,15 @@ public class SyntacticSequencerPDAProviderTest extends AbstractXtextTests {
public void drawGrammar(String path, Grammar grammar) {
try {
IContextProvider contexts = get(IContextProvider.class);
IContextPDAProvider contexts = get(IContextPDAProvider.class);
IContextTypePDAProvider types = get(IContextTypePDAProvider.class);
SyntacticSequencerPDA2ExtendedDot seq2dot = get(SyntacticSequencerPDA2ExtendedDot.class);
for (EObject ctx : contexts.getAllContexts(grammar))
for (EClass type : contexts.getTypesForContext(ctx))
seq2dot.draw(
new Pair<EObject, EClass>(ctx, type),
for (EClass type : types.getTypesForContext(grammar, ctx))
seq2dot.draw(new Pair<EObject, EClass>(ctx, type),
path + "-" + new Context2NameFunction().toFunction(grammar).apply(ctx) + "_"
+ (type == null ? "null" : type.getName()) + "-PDA.pdf", "-T pdf");
+ (type == null ? "null" : type.getName()) + "-PDA.pdf",
"-T pdf");
} catch (IOException e) {
e.printStackTrace();
}
@ -86,10 +88,11 @@ public class SyntacticSequencerPDAProviderTest extends AbstractXtextTests {
private List<Triple<EClass, EObject, String>> getContexts(Grammar grammar) {
final Context2NameFunction ctx2name = get(Context2NameFunction.class);
final IContextProvider contextProvider = get(IContextProvider.class);
final IContextPDAProvider contextProvider = get(IContextPDAProvider.class);
final IContextTypePDAProvider typeProvider = get(IContextTypePDAProvider.class);
List<Triple<EClass, EObject, String>> result = Lists.newArrayList();
for (EObject ctx : contextProvider.getAllContexts(grammar))
for (EClass type : contextProvider.getTypesForContext(ctx))
for (EClass type : typeProvider.getTypesForContext(grammar, ctx))
result.add(Tuples.create(type, ctx, ctx2name.getContextName(grammar, ctx)));
Collections.sort(result, new Comparator<Triple<EClass, EObject, String>>() {
@Override
@ -697,7 +700,11 @@ public class SyntacticSequencerPDAProviderTest extends AbstractXtextTests {
expected.append(" start '}'* greetings2+=Greeting2\n");
expected.append(" start '}'+ stop\n");
expected.append("null_AllElements:\n");
expected.append(" start stop");
expected.append(" start stop\n");
expected.append("null_Elements:\n");
expected.append(" start >>Model '}'+ <<Model stop\n");
expected.append("null_Model:\n");
expected.append(" start '}'+ stop");
assertEquals(expected.toString(), actual);
}

View file

@ -400,4 +400,9 @@ public class SyntacticSequencerTest extends AbstractXtextTests {
public void testActionOnlyWithOptionals() throws Exception {
testSequence("#14 kw1 foo kw2 bar");
}
@Test
public void testFragment() throws Exception {
testSequence("#15 foo bar baz");
}
}

View file

@ -13,7 +13,7 @@ generate syntacticsequencertest "http://www.eclipse.org/2009/tmf/xtext/syntactic
Model:
x1=MandatoryKeywords | x2=Exp0 | x3=Exp1 | x4=Exp2 | x5=SingleCrossReference | x6=BooleanAlternative |
x7=UnassignedDatatype | x8=OptionalSingleTransition | x9=OptionalManyTransition | x10=MandatoryManyTransition |
x11=AlternativeTransition | x12=BooleanValues | x13=LongAlternative | x14=ActionOnly;
x11=AlternativeTransition | x12=BooleanValues | x13=LongAlternative | x14=ActionOnly | x15=FragmentCaller;
MandatoryKeywords:
"#1" val1=ID "kw1" val2=ID "kw2" "kw3" val3=ID "kw4";
@ -105,7 +105,12 @@ LongAlternative:
"kw7" val7+=ID? |
"kw8" val8+=ID?)*
'!';
ActionOnly:
"#14" "kw1"? =>ID? {ActionOnly} "kw2"? ID?;
"#14" "kw1"? =>ID? {ActionOnly} "kw2"? ID?;
FragmentCaller returns FragmentCallerType:
"#15" val1=ID Fragment1 val=ID;
fragment Fragment1 returns FragmentCallerType:
fragVal=ID;