From fb3e180f8ad77ac406cfc72b06907042d0a642c0 Mon Sep 17 00:00:00 2001 From: Moritz Eysholdt Date: Mon, 28 Sep 2015 14:43:27 +0200 Subject: [PATCH] added support for grammar fragments and refactored pda providers Signed-off-by: Moritz Eysholdt --- .../xtext/util/formallang/NfaUtil.java | 6 +- .../xtext/util/formallang/PdaUtil.java | 126 +++-- .../analysis/ContextPDAProvider.java | 373 ++++++++------- .../serializer/analysis/ContextProvider.java | 68 +-- .../analysis/ContextTypePDAProvider.java | 77 +++- .../analysis/GrammarConstraintProvider.java | 7 +- .../analysis/GrammarPDAProvider.java | 105 +++++ .../analysis/IContextPDAProvider.java | 7 +- .../serializer/analysis/IContextProvider.java | 13 +- .../analysis/IContextTypePDAProvider.java | 7 +- .../analysis/IGrammarPDAProvider.java | 28 ++ .../xtext/serializer/analysis/ISerState.java | 4 + .../serializer/analysis/SerializerPDA.java | 41 +- .../SyntacticSequencerPDAProvider.java | 4 +- .../AbstractSemanticSequencerTest.java | 8 +- .../serializer/ContextPDAProviderTest.java | 173 +++++-- .../xtext/serializer/ContextProviderTest.java | 42 +- .../ContextTypePDAProviderTest.java | 30 +- .../serializer/GrammarPDAProviderTest.xtend | 432 ++++++++++++++++++ .../serializer/SequencerTestLanguage.xtext | 11 +- .../SyntacticSequencerPDAProviderTest.java | 25 +- .../serializer/SyntacticSequencerTest.java | 5 + .../SyntacticSequencerTestLanguage.xtext | 11 +- 23 files changed, 1192 insertions(+), 411 deletions(-) create mode 100644 plugins/org.eclipse.xtext/src/org/eclipse/xtext/serializer/analysis/GrammarPDAProvider.java create mode 100644 plugins/org.eclipse.xtext/src/org/eclipse/xtext/serializer/analysis/IGrammarPDAProvider.java create mode 100644 tests/org.eclipse.xtext.tests/src/org/eclipse/xtext/serializer/GrammarPDAProviderTest.xtend diff --git a/plugins/org.eclipse.xtext.util/src/org/eclipse/xtext/util/formallang/NfaUtil.java b/plugins/org.eclipse.xtext.util/src/org/eclipse/xtext/util/formallang/NfaUtil.java index d66b208c2..96e1a0d30 100644 --- a/plugins/org.eclipse.xtext.util/src/org/eclipse/xtext/util/formallang/NfaUtil.java +++ b/plugins/org.eclipse.xtext.util/src/org/eclipse/xtext/util/formallang/NfaUtil.java @@ -168,6 +168,10 @@ public class NfaUtil { return null; } + public > boolean canReach(Nfa nfa, Predicate matcher) { + return find(nfa, Collections.singleton(nfa.getStart()), matcher) != null; + } + public > boolean canReach(Nfa nfa, S state, Predicate matcher) { return find(nfa, Collections.singleton(state), matcher) != null; } @@ -513,5 +517,5 @@ public class NfaUtil { public > Nfa sort(Nfa nfa, Map comparator) { return sort(nfa, new MappedComparator(comparator)); } - + } diff --git a/plugins/org.eclipse.xtext.util/src/org/eclipse/xtext/util/formallang/PdaUtil.java b/plugins/org.eclipse.xtext.util/src/org/eclipse/xtext/util/formallang/PdaUtil.java index 50f0d7b64..a011cbabb 100644 --- a/plugins/org.eclipse.xtext.util/src/org/eclipse/xtext/util/formallang/PdaUtil.java +++ b/plugins/org.eclipse.xtext.util/src/org/eclipse/xtext/util/formallang/PdaUtil.java @@ -278,7 +278,8 @@ public class PdaUtil { public final long UNREACHABLE = Long.MAX_VALUE; - public boolean canReach(Pda pda, S state, Iterator

stack, Predicate matches, Predicate canPass) { + public boolean canReach(Pda pda, S state, Iterator

stack, Predicate matches, + Predicate canPass) { return distanceTo(pda, Collections.singleton(state), stack, matches, canPass) != UNREACHABLE; } @@ -594,6 +595,26 @@ public class PdaUtil { return null; } + public List collectReachable(Pda pda, final Function function) { + final List result = Lists.newArrayList(); + Iterator

stack = Collections.

emptyList().iterator(); + Predicate matches = Predicates. alwaysFalse(); + Predicate canPass = new Predicate() { + @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 TraceItem trace(Pda pda, Iterable starts, Iterator

stack, Predicate matches, Predicate canPass) { StackItem

stackItem = createStack(stack); @@ -693,75 +714,36 @@ public class PdaUtil { return filterEdges(pda, traverser, factory); } - // public IPdaAdapter 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 starts = Sets.newLinkedHashSet(); - // final Set finals = Sets.newLinkedHashSet(); - // final Multimap followers = HashMultimap.create(); - // final Map pushs = Maps.newLinkedHashMap(); - // final Map 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() { - // - // public String getToken(String owner) { - // return null; - // } - // - // public Iterable getStartStates() { - // return starts; - // } - // - // public Iterable getFollowers(String node) { - // return followers.get(node); - // } - // - // public Iterable getFinalStates() { - // return finals; - // } - // - // public String getPush(String state) { - // return pushs.get(state); - // } - // - // public String getPop(String state) { - // return pops.get(state); - // } - // }; - // } + public > Map> collectPopsAndPushs(Pda pda) { + Map> result = Maps.newLinkedHashMap(); + for (S s : nfaUtil.collect(pda)) { + P push = pda.getPush(s); + if (push != null) { + Pair o = result.get(push); + Pair n = Tuples.create(s, o == null ? null : o.getSecond()); + result.put(push, n); + } + P pop = pda.getPop(s); + if (pop != null) { + Pair o = result.get(pop); + Pair n = Tuples.create(o == null ? null : o.getFirst(), s); + result.put(pop, n); + } + } + return result; + } + + public > Map mapPopAndPush(Pda pda) { + Map> popsAndPushs = collectPopsAndPushs(pda); + Map result = Maps.newLinkedHashMap(); + for (Pair 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; + } } diff --git a/plugins/org.eclipse.xtext/src/org/eclipse/xtext/serializer/analysis/ContextPDAProvider.java b/plugins/org.eclipse.xtext/src/org/eclipse/xtext/serializer/analysis/ContextPDAProvider.java index 0b7ffd36e..c91fffa5e 100644 --- a/plugins/org.eclipse.xtext/src/org/eclipse/xtext/serializer/analysis/ContextPDAProvider.java +++ b/plugins/org.eclipse.xtext/src/org/eclipse/xtext/serializer/analysis/ContextPDAProvider.java @@ -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> { - @Override - public Pda 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 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>> 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 { - - protected Action actionCtx; - - public SerializerActionFollowerFunction(Production production, Action context) { - super(production); - this.actionCtx = context; - } - - @Override - public Iterable getFollowers(AbstractElement element) { - Set 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 getStarts(AbstractElement root) { - Set 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 { - - protected ParserRule ruleCtx; - - public SerializerParserRuleFollowerFunction(Production production, - ParserRule context) { - super(production); - this.ruleCtx = context; - } - - @Override - public Iterable getFollowers(AbstractElement element) { - Set 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 getStarts(AbstractElement root) { - Set 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> cache = Maps.newHashMap(); + @Inject + private NfaUtil nfaUtil; @Inject protected PdaUtil pdaUtil; - protected Pda createPDA(Action action) { - SerializerActionCfg cfg = new SerializerActionCfg(action); - SerializerActionFollowerFunction ff = new SerializerActionFollowerFunction(cfg, action); - SerializerPDAElementFactory fact = new SerializerPDAElementFactory(); - Pda actionpda = pdaUtil.create(cfg, ff, fact); - SerializerPDAGetToken getToken = new SerializerPDAGetToken(); - Pda expandedpda = pdaUtil.expand(actionpda, new ExpandRuleCalls(), getToken, fact); - Pda filteredpda = pdaUtil.filterOrphans(expandedpda, new SerializerPDACloneFactory()); - return filteredpda; + protected void collectExtracted(ISerState orig, Collection precedents, SerializerPDAState copy, + Map, 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 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 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 createPDA(EObject context, Pda 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 createPDA(ParserRule rule) { - SerializerParserRuleCfg cfg = new SerializerParserRuleCfg(rule); - SerializerParserRuleFollowerFunction ff = new SerializerParserRuleFollowerFunction(cfg, rule); - Pda pda = pdaUtil.create(cfg, ff, new SerializerPDAElementFactory()); - return pdaUtil.filterOrphans(pda, new SerializerPDACloneFactory()); - } - - @Override - public Pda getContextPDA(EObject context) { - Pda result = cache.get(context); - if (result == null) - cache.put(context, result = createPDA(context, result)); + protected Set collectPushForAction(ISerState action) { + ParserRule rule = GrammarUtil.containingParserRule(action.getGrammarElement()); + LinkedHashSet result = Sets. newLinkedHashSet(); + collectPushForAction(action, rule, result, Sets. newHashSet()); return result; } + protected void collectPushForAction(ISerState state, ParserRule rule, Set result, + Set 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 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 extract(ISerState last) { + SerializerPDA result = factory.create(null, null); + HashMap, 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 getAllContexts(Grammar grammar) { + Map> map = getOrCreate(grammar); + Set 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 getContextPDA(Grammar grammar, EObject context) { + Map> map = getOrCreate(grammar); + Pda pda = map.get(context); + if (pda == null) + throw new IllegalStateException("Invlid context for serialization: " + context); + return pda; + } + + protected Map> getOrCreate(Grammar grammar) { + Map> result = cache.get(grammar); + if (result != null) + return result; + result = Maps.newLinkedHashMap(); + Set allParserRules = grammarPdaProvider.getAllRules(grammar); + Multimap> actionPdas = HashMultimap.create(); + for (ParserRule rule : allParserRules) { + Pda pda = grammarPdaProvider.getGrammarPDA(grammar, rule); + List 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 rulePda = extract(pda.getStop()); + result.put(rule, rulePda); + for (ISerState action : actions) { + Pda actionPda = extract(action); + actionPdas.put((Action) action.getGrammarElement(), actionPda); + } + } + } + for (Map.Entry>> action : actionPdas.asMap().entrySet()) { + Pda merged = merge(action.getValue()); + result.put(action.getKey(), merged); + } + cache.put(grammar, result); + return result; + } + + protected Pda merge(Collection> pdas) { + if (pdas.isEmpty()) + throw new IllegalStateException(); + if (pdas.size() == 1) + return pdas.iterator().next(); + SerializerPDA merged = factory.create(null, null); + Map oldToNew = Maps.newHashMap(); + for (Pda pda : pdas) { + oldToNew.put(pda.getStop(), merged.getStop()); + merge(pda.getStart(), merged.getStart(), oldToNew, new IdentityHashMap()); + } + return merged; + } + + protected void merge(ISerState orig, SerializerPDAState copy, Map oldToNew, + IdentityHashMap 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); + } + } + } diff --git a/plugins/org.eclipse.xtext/src/org/eclipse/xtext/serializer/analysis/ContextProvider.java b/plugins/org.eclipse.xtext/src/org/eclipse/xtext/serializer/analysis/ContextProvider.java index 6ed82d083..849518086 100644 --- a/plugins/org.eclipse.xtext/src/org/eclipse/xtext/serializer/analysis/ContextProvider.java +++ b/plugins/org.eclipse.xtext/src/org/eclipse/xtext/serializer/analysis/ContextProvider.java @@ -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 types, boolean allowLocal, - boolean hasAssignment, Set 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 getAllContexts(Grammar grammar) { - List 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 getTypesForContext(EObject context) { - Set 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 getTypesForContext(Grammar grammar, EObject context) { + return typeProvider.getTypesForContext(grammar, context); } - } diff --git a/plugins/org.eclipse.xtext/src/org/eclipse/xtext/serializer/analysis/ContextTypePDAProvider.java b/plugins/org.eclipse.xtext/src/org/eclipse/xtext/serializer/analysis/ContextTypePDAProvider.java index 126672a95..b5d932bcf 100644 --- a/plugins/org.eclipse.xtext/src/org/eclipse/xtext/serializer/analysis/ContextTypePDAProvider.java +++ b/plugins/org.eclipse.xtext/src/org/eclipse/xtext/serializer/analysis/ContextTypePDAProvider.java @@ -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, ISerState, FilterState> { + protected class TypeFilter implements Traverser, 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, Pda> cache = Maps.newHashMap(); - @Inject - protected IContextProvider contextProvider; @Inject protected IContextPDAProvider pdaProvider; - protected Pda createPDA(EObject context, EClass type) { - Pda contextPda = pdaProvider.getContextPDA(context); + @Inject + protected PdaUtil pdaUtil; + + protected Pda createPDA(Grammar grammar, EObject context, EClass type) { + Pda contextPda = pdaProvider.getContextPDA(grammar, context); Pda 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 getContextTypePDA(EObject context, EClass type) { + public Pda getContextTypePDA(Grammar grammar, EObject context, EClass type) { Pair key = Tuples.create(context, type); Pda 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 getTypesForContext(Grammar grammar, EObject context) { + Pda contextPda = pdaProvider.getContextPDA(grammar, context); + final Wrapper canReachStop = new Wrapper(false); + List list = pdaUtil.collectReachable(contextPda, new Function() { + @Override + public EClass apply(ISerState input) { + if (input.getType() == SerStateType.STOP) + canReachStop.set(true); + return getInstantiatedType(input.getGrammarElement()); + } + }); + Set result = Sets.newLinkedHashSet(list); + if (canReachStop.get()) + result.add(null); return result; } diff --git a/plugins/org.eclipse.xtext/src/org/eclipse/xtext/serializer/analysis/GrammarConstraintProvider.java b/plugins/org.eclipse.xtext/src/org/eclipse/xtext/serializer/analysis/GrammarConstraintProvider.java index 703c58431..e1c0a0ee6 100644 --- a/plugins/org.eclipse.xtext/src/org/eclipse/xtext/serializer/analysis/GrammarConstraintProvider.java +++ b/plugins/org.eclipse.xtext/src/org/eclipse/xtext/serializer/analysis/GrammarConstraintProvider.java @@ -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 types = contextProvider.getTypesForContext(context); + Set 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 types = contextProvider.getTypesForContext(context); + Set types = contextProvider.getTypesForContext(grammar, context); for (EClass type : types) { if (type == null) { Constraint constraint = new RuleConstraint(context, null, null, this); diff --git a/plugins/org.eclipse.xtext/src/org/eclipse/xtext/serializer/analysis/GrammarPDAProvider.java b/plugins/org.eclipse.xtext/src/org/eclipse/xtext/serializer/analysis/GrammarPDAProvider.java new file mode 100644 index 000000000..c22ff5ab8 --- /dev/null +++ b/plugins/org.eclipse.xtext/src/org/eclipse/xtext/serializer/analysis/GrammarPDAProvider.java @@ -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 { + + public SerializerParserRuleFollowerFunction(Production production) { + super(production); + } + + } + + protected Map> cache = Maps.newHashMap(); + + @Inject + protected PdaUtil pdaUtil; + + protected Pda createPDA(Grammar grammar, ParserRule entryRule) { + Preconditions.checkArgument(isValidRule(entryRule)); + SerializerParserRuleCfg cfg = new SerializerParserRuleCfg(grammar, entryRule); + SerializerParserRuleFollowerFunction ff = new SerializerParserRuleFollowerFunction(cfg); + Pda pda = pdaUtil.create(cfg, ff, new SerializerPDAElementFactory()); + // return pdaUtil.filterOrphans(pda, new SerializerPDACloneFactory()); + return pda; + } + + @Override + public Pda getGrammarPDA(Grammar grammar, ParserRule entryRule) { + Pda 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 getAllRules(Grammar grammar) { + Set result = Sets.newLinkedHashSet(); + for (ParserRule rule : GrammarUtil.allParserRules(grammar)) + if (isValidRule(rule)) + result.add(rule); + return result; + } + +} diff --git a/plugins/org.eclipse.xtext/src/org/eclipse/xtext/serializer/analysis/IContextPDAProvider.java b/plugins/org.eclipse.xtext/src/org/eclipse/xtext/serializer/analysis/IContextPDAProvider.java index 39a97700b..4139c6cf8 100644 --- a/plugins/org.eclipse.xtext/src/org/eclipse/xtext/serializer/analysis/IContextPDAProvider.java +++ b/plugins/org.eclipse.xtext/src/org/eclipse/xtext/serializer/analysis/IContextPDAProvider.java @@ -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 getContextPDA(EObject context); + Pda getContextPDA(Grammar grammar, EObject context); + + Set getAllContexts(Grammar grammar); } diff --git a/plugins/org.eclipse.xtext/src/org/eclipse/xtext/serializer/analysis/IContextProvider.java b/plugins/org.eclipse.xtext/src/org/eclipse/xtext/serializer/analysis/IContextProvider.java index ded987344..1ebdb4686 100644 --- a/plugins/org.eclipse.xtext/src/org/eclipse/xtext/serializer/analysis/IContextProvider.java +++ b/plugins/org.eclipse.xtext/src/org/eclipse/xtext/serializer/analysis/IContextProvider.java @@ -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 getAllContexts(Grammar grammar); - public Set getTypesForContext(EObject context); + /** + * @deprecated use {@link IContextTypePDAProvider#getTypesForContext(Grammar, EObject)} + */ + @Deprecated + public Set getTypesForContext(Grammar grammar, EObject context); } diff --git a/plugins/org.eclipse.xtext/src/org/eclipse/xtext/serializer/analysis/IContextTypePDAProvider.java b/plugins/org.eclipse.xtext/src/org/eclipse/xtext/serializer/analysis/IContextTypePDAProvider.java index f5721b53d..64878a5d6 100644 --- a/plugins/org.eclipse.xtext/src/org/eclipse/xtext/serializer/analysis/IContextTypePDAProvider.java +++ b/plugins/org.eclipse.xtext/src/org/eclipse/xtext/serializer/analysis/IContextTypePDAProvider.java @@ -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 getContextTypePDA(EObject context, EClass type); + Pda getContextTypePDA(Grammar grammar, EObject context, EClass type); + + public Set getTypesForContext(Grammar grammar, EObject context); } diff --git a/plugins/org.eclipse.xtext/src/org/eclipse/xtext/serializer/analysis/IGrammarPDAProvider.java b/plugins/org.eclipse.xtext/src/org/eclipse/xtext/serializer/analysis/IGrammarPDAProvider.java new file mode 100644 index 000000000..3f812524e --- /dev/null +++ b/plugins/org.eclipse.xtext/src/org/eclipse/xtext/serializer/analysis/IGrammarPDAProvider.java @@ -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 getGrammarPDA(Grammar grammar, ParserRule entryRule); + + Set getAllRules(Grammar grammar); +} diff --git a/plugins/org.eclipse.xtext/src/org/eclipse/xtext/serializer/analysis/ISerState.java b/plugins/org.eclipse.xtext/src/org/eclipse/xtext/serializer/analysis/ISerState.java index d6e06b9c4..295b2479b 100644 --- a/plugins/org.eclipse.xtext/src/org/eclipse/xtext/serializer/analysis/ISerState.java +++ b/plugins/org.eclipse.xtext/src/org/eclipse/xtext/serializer/analysis/ISerState.java @@ -16,8 +16,12 @@ public interface ISerState { ELEMENT, POP, PUSH, START, STOP; } + // ISerState getOpposite(); + List getFollowers(); + List getPrecedents(); + AbstractElement getGrammarElement(); SerStateType getType(); diff --git a/plugins/org.eclipse.xtext/src/org/eclipse/xtext/serializer/analysis/SerializerPDA.java b/plugins/org.eclipse.xtext/src/org/eclipse/xtext/serializer/analysis/SerializerPDA.java index 3662644e7..fdf4df05e 100644 --- a/plugins/org.eclipse.xtext/src/org/eclipse/xtext/serializer/analysis/SerializerPDA.java +++ b/plugins/org.eclipse.xtext/src/org/eclipse/xtext/serializer/analysis/SerializerPDA.java @@ -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 { @@ -54,8 +55,8 @@ public class SerializerPDA implements Pda { } } - public static class SerializerPDAElementFactory implements - PdaFactory { + public static class SerializerPDAElementFactory + implements PdaFactory { @Override public SerializerPDA create(AbstractElement start, AbstractElement stop) { @@ -82,6 +83,9 @@ public class SerializerPDA implements Pda { @Override public void setFollowers(SerializerPDA nfa, ISerState owner, Iterable 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 { } + public static class SerializerPDAIsAssignedAction implements Predicate { + + @Override + public boolean apply(ISerState input) { + return input != null && GrammarUtil.isAssignedAction(input.getGrammarElement()); + } + + } + protected static class SerializerPDAState implements ISerState { - protected List followers = Collections.emptyList(); - protected AbstractElement grammarElement; - protected SerStateType type; + protected List followers = Lists.newArrayList(); + protected final List 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 { } return ""; } + + @Override + public List getPrecedents() { + return precedents; + } + + // @Override + // public ISerState getOpposite() { + // return null; + // } } protected SerializerPDA.SerializerPDAState start; @@ -187,12 +212,12 @@ public class SerializerPDA implements Pda { } @Override - public ISerState getStart() { + public SerializerPDAState getStart() { return start; } @Override - public ISerState getStop() { + public SerializerPDAState getStop() { return stop; } diff --git a/plugins/org.eclipse.xtext/src/org/eclipse/xtext/serializer/analysis/SyntacticSequencerPDAProvider.java b/plugins/org.eclipse.xtext/src/org/eclipse/xtext/serializer/analysis/SyntacticSequencerPDAProvider.java index abbff3f01..aaf56b7da 100644 --- a/plugins/org.eclipse.xtext/src/org/eclipse/xtext/serializer/analysis/SyntacticSequencerPDAProvider.java +++ b/plugins/org.eclipse.xtext/src/org/eclipse/xtext/serializer/analysis/SyntacticSequencerPDAProvider.java @@ -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 absorbers = Maps.newHashMap(); Map> emitters = Maps.newHashMap(); - Pda pda = pdaProvider.getContextTypePDA(context, type); + Grammar grammar = GrammarUtil.getGrammar(context); + Pda pda = pdaProvider.getContextTypePDA(grammar, context, type); result = createAbsorberState(pda.getStart(), absorbers, emitters, context, type); cache.put(key, result); } diff --git a/tests/org.eclipse.xtext.tests/src/org/eclipse/xtext/serializer/AbstractSemanticSequencerTest.java b/tests/org.eclipse.xtext.tests/src/org/eclipse/xtext/serializer/AbstractSemanticSequencerTest.java index cc360f300..2ed0ab55c 100644 --- a/tests/org.eclipse.xtext.tests/src/org/eclipse/xtext/serializer/AbstractSemanticSequencerTest.java +++ b/tests/org.eclipse.xtext.tests/src/org/eclipse/xtext/serializer/AbstractSemanticSequencerTest.java @@ -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"); + } -} \ No newline at end of file + +} diff --git a/tests/org.eclipse.xtext.tests/src/org/eclipse/xtext/serializer/ContextPDAProviderTest.java b/tests/org.eclipse.xtext.tests/src/org/eclipse/xtext/serializer/ContextPDAProviderTest.java index 2564a417e..1d4b953aa 100644 --- a/tests/org.eclipse.xtext.tests/src/org/eclipse/xtext/serializer/ContextPDAProviderTest.java +++ b/tests/org.eclipse.xtext.tests/src/org/eclipse/xtext/serializer/ContextPDAProviderTest.java @@ -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> getContexts(Grammar grammar) { final Context2NameFunction ctx2name = get(Context2NameFunction.class); - final IContextProvider contextProvider = get(IContextProvider.class); + final IContextPDAProvider contextProvider = get(IContextPDAProvider.class); List> 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 ctx : getContexts(grammar)) { + List> contexts = getContexts(grammar); + for (Pair ctx : contexts) { result.add(ctx.getSecond() + ":"); - Pda pda = pdaProvider.getContextPDA(ctx.getFirst()); + Pda 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().draw(pda, name, "-T pdf"); + + // StackTraceElement ele = Thread.currentThread().getStackTrace()[2]; + // String name = getClass().getSimpleName() + "_" + ele.getMethodName() + "_" + ctx.getSecond() + ".pdf"; + // new PdaToDot().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(" < stop\n"); + expected.append(" >>F -> {A.prev=}\n"); + expected.append(" f2=ID -> < 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(" < f1=ID\n"); + expected.append(" >>F -> {A.prev=}\n"); + expected.append(" f1=ID -> stop\n"); + expected.append(" f2=ID -> < f2=ID\n"); + expected.append("R:\n"); + expected.append(" start -> 'kw1'\n"); + expected.append(" 'kw1' -> >>F\n"); + expected.append(" < stop\n"); + expected.append(" >>F -> {A.prev=}\n"); + expected.append(" f2=ID -> < 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(" < stop\n"); + expected.append(" >>F -> {B.prev=}\n"); + expected.append(" f3=ID -> < 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(" < stop\n"); + expected.append(" >>F -> < >>F\n"); + expected.append(" f2=ID -> < 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(" < stop\n"); + expected.append(" < stop\n"); + expected.append(" >>Rule1 -> foo1=ID\n"); + expected.append(" >>Rule2 -> foo2=ID\n"); + expected.append(" foo1=ID -> < < 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"); diff --git a/tests/org.eclipse.xtext.tests/src/org/eclipse/xtext/serializer/ContextProviderTest.java b/tests/org.eclipse.xtext.tests/src/org/eclipse/xtext/serializer/ContextProviderTest.java index 6fa5e98d9..9de8ac35a 100644 --- a/tests/org.eclipse.xtext.tests/src/org/eclipse/xtext/serializer/ContextProviderTest.java +++ b/tests/org.eclipse.xtext.tests/src/org/eclipse/xtext/serializer/ContextProviderTest.java @@ -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>> result = Lists.newArrayList(); for (EObject context : contextProvider.getAllContexts(grammar)) { List 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); } diff --git a/tests/org.eclipse.xtext.tests/src/org/eclipse/xtext/serializer/ContextTypePDAProviderTest.java b/tests/org.eclipse.xtext.tests/src/org/eclipse/xtext/serializer/ContextTypePDAProviderTest.java index de7c698dd..c5fa4936b 100644 --- a/tests/org.eclipse.xtext.tests/src/org/eclipse/xtext/serializer/ContextTypePDAProviderTest.java +++ b/tests/org.eclipse.xtext.tests/src/org/eclipse/xtext/serializer/ContextTypePDAProviderTest.java @@ -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> 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> 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>() { @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 pda = get(IContextTypePDAProvider.class).getContextTypePDA( + Pda pda = get(IContextTypePDAProvider.class).getContextTypePDA(grammar, ctx.getSecond(), ctx.getFirst()); result.add(" " + formatter.format((Pda) 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); + } } diff --git a/tests/org.eclipse.xtext.tests/src/org/eclipse/xtext/serializer/GrammarPDAProviderTest.xtend b/tests/org.eclipse.xtext.tests/src/org/eclipse/xtext/serializer/GrammarPDAProviderTest.xtend new file mode 100644 index 000000000..274a05086 --- /dev/null +++ b/tests/org.eclipse.xtext.tests/src/org/eclipse/xtext/serializer/GrammarPDAProviderTest.xtend @@ -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 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 + < < < stop + < stop + >>A -> f1=ID + >>A -> f1=ID + >>D1 -> 'd1' + >>D2 -> 'd2' + f1=ID -> {A.l=} + f2=ID -> < f2=ID + D1: + start -> 'd1' + 'd1' -> >>A + < stop + >>A -> f1=ID + f1=ID -> {A.l=} + f2=ID -> < f2=ID + D2: + start -> 'd2' + 'd2' -> >>A + < stop + >>A -> f1=ID + f1=ID -> {A.l=} + f2=ID -> < 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 + < stop + >>Called -> name=ID + name=ID -> < >>Called + < stop + >>Called -> name=ID + name=ID -> < 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 + < stop + >>Called -> name=ID + name=ID -> < {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 -> '(', val=ID + val=ID -> < 'kw' + '+' -> right=Prim + 'kw' -> >>Addit + < stop + < <>Addit -> >>Prim + >>Prim -> {Val} + right=Prim -> < < '+' + {Val} -> val=ID + Addit: + start -> >>Prim + '+' -> right=Prim + < stop, {Add.left=} + >>Prim -> {Val} + right=Prim -> stop, {Add.left=} + val=ID -> < '+' + {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 -> '(', val=ID + val=ID -> < < f1=ID + < stop + >>F -> {A.prev=} + f1=ID -> >>F + f2=ID -> < 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 + < stop + >>F -> {A.prev=} + f1=ID -> >>F + f2=ID -> < f2=ID + ''' + Assert.assertEquals(expected, actual) + } + + @Test @Ignore def void testParameter1() { + val actual = ''' + M: "kw1" s=S | "kw2" s=S; + S

:

v1=ID | v2=ID; + '''.toPda + val expected = ''' + Greeting: + start -> '(', val=ID + '(' -> >>Greeting + ')' -> {Foo.child=} + < ')' + >>Greeting -> '(', val=ID + val=ID -> < < 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 pda) { + val ts = new GrammarElementTitleSwitch().showAssignments().hideCardinality().showQualified() + val formatter = new PdaListFormatter(); + 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" + } +} \ No newline at end of file diff --git a/tests/org.eclipse.xtext.tests/src/org/eclipse/xtext/serializer/SequencerTestLanguage.xtext b/tests/org.eclipse.xtext.tests/src/org/eclipse/xtext/serializer/SequencerTestLanguage.xtext index 2b52f52bb..f471e47cc 100644 --- a/tests/org.eclipse.xtext.tests/src/org/eclipse/xtext/serializer/SequencerTestLanguage.xtext +++ b/tests/org.eclipse.xtext.tests/src/org/eclipse/xtext/serializer/SequencerTestLanguage.xtext @@ -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?; \ No newline at end of file + "#32" ref=[ecore::EObject|ID] foo=ID?; + +FragmentCaller returns FragmentCallerType: + "#33" val1=ID Fragment1 val=ID; + +fragment Fragment1 returns FragmentCallerType: + fragVal=ID; \ No newline at end of file diff --git a/tests/org.eclipse.xtext.tests/src/org/eclipse/xtext/serializer/SyntacticSequencerPDAProviderTest.java b/tests/org.eclipse.xtext.tests/src/org/eclipse/xtext/serializer/SyntacticSequencerPDAProviderTest.java index 9a3e90e7b..9b1ab2367 100644 --- a/tests/org.eclipse.xtext.tests/src/org/eclipse/xtext/serializer/SyntacticSequencerPDAProviderTest.java +++ b/tests/org.eclipse.xtext.tests/src/org/eclipse/xtext/serializer/SyntacticSequencerPDAProviderTest.java @@ -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(ctx, type), + for (EClass type : types.getTypesForContext(grammar, ctx)) + seq2dot.draw(new Pair(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> 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> 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>() { @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 '}'+ <ID? {ActionOnly} "kw2"? ID?; \ No newline at end of file + "#14" "kw1"? =>ID? {ActionOnly} "kw2"? ID?; + +FragmentCaller returns FragmentCallerType: + "#15" val1=ID Fragment1 val=ID; + +fragment Fragment1 returns FragmentCallerType: + fragVal=ID; \ No newline at end of file