mirror of
https://github.com/sigmasternchen/xtext-core
synced 2025-03-16 08:48:55 +00:00
added support for grammar fragments and refactored pda providers
Signed-off-by: Moritz Eysholdt <moritz.eysholdt@itemis.de>
This commit is contained in:
parent
2b95508568
commit
fb3e180f8a
23 changed files with 1192 additions and 411 deletions
|
@ -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));
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
|
@ -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();
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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"
|
||||
}
|
||||
}
|
|
@ -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;
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
Loading…
Reference in a new issue