mirror of
https://github.com/sigmasternchen/xtext-core
synced 2025-03-17 01:08:56 +00:00
Merge pull request #666 from eclipse/me/bug479397
[serializer] fixed 479397: compute types of wildcard fragments properly
This commit is contained in:
commit
42cbaa0c25
5 changed files with 127 additions and 64 deletions
|
@ -427,7 +427,7 @@ public class PdaUtil {
|
|||
HashStack<TraversalItem<S, R>> trace = new HashStack<TraversalItem<S, R>>();
|
||||
R previous = traverser.enter(pda, pda.getStart(), null);
|
||||
if (previous == null)
|
||||
return factory.create(pda.getStart(), pda.getStop());
|
||||
return factory == null ? null : factory.create(pda.getStart(), pda.getStop());
|
||||
Map<S, Integer> distances = new NfaUtil().distanceToFinalStateMap(pda);
|
||||
MappedComparator<S, Integer> distanceComp = new MappedComparator<S, Integer>(distances);
|
||||
trace.push(newItem(pda, distanceComp, distances, pda.getStart(), previous));
|
||||
|
@ -461,6 +461,8 @@ public class PdaUtil {
|
|||
}
|
||||
trace.pop();
|
||||
}
|
||||
if (factory == null)
|
||||
return null;
|
||||
D result = factory.create(pda.getStart(), pda.getStop());
|
||||
Map<S, S> old2new = Maps.newLinkedHashMap();
|
||||
old2new.put(pda.getStart(), result.getStart());
|
||||
|
|
|
@ -7,7 +7,6 @@
|
|||
*******************************************************************************/
|
||||
package org.eclipse.xtext.serializer.analysis;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
|
@ -15,22 +14,21 @@ 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.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.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;
|
||||
|
@ -42,6 +40,68 @@ import com.google.inject.Singleton;
|
|||
@Singleton
|
||||
public class ContextTypePDAProvider implements IContextTypePDAProvider {
|
||||
|
||||
protected static abstract class AbstractTypeTraverser
|
||||
implements Traverser<Pda<ISerState, RuleCall>, ISerState, FilterState> {
|
||||
|
||||
@Override
|
||||
public FilterState enter(Pda<ISerState, RuleCall> pda, ISerState state, FilterState previous) {
|
||||
switch (state.getType()) {
|
||||
case ELEMENT:
|
||||
if (previous.type == null) {
|
||||
EClass cls = getInstantiatedType(state.getGrammarElement());
|
||||
if (cls != null)
|
||||
return enterType(state, previous, previous.stack, cls);
|
||||
} else if (state.getGrammarElement() instanceof Action)
|
||||
return null;
|
||||
return new FilterState(previous, previous.type, previous.stack, state);
|
||||
case POP:
|
||||
if (previous.stack != null && state.getGrammarElement() == previous.stack.rc)
|
||||
return new FilterState(previous, previous.type, previous.stack.parent, state);
|
||||
return null;
|
||||
case PUSH:
|
||||
RuleCall rc = (RuleCall) state.getGrammarElement();
|
||||
EClass cls = getInstantiatedType(rc);
|
||||
if (cls != null)
|
||||
return enterType(state, previous, new StackItem(previous.stack, rc), cls);
|
||||
return new FilterState(previous, previous.type, new StackItem(previous.stack, rc), state);
|
||||
case START:
|
||||
return new FilterState(previous, null, null, state);
|
||||
case STOP:
|
||||
if (previous.stack == null)
|
||||
return previous;
|
||||
return null;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
protected abstract FilterState enterType(ISerState state, FilterState previous, StackItem stack,
|
||||
EClass newType);
|
||||
|
||||
protected EClass getInstantiatedType(AbstractElement element) {
|
||||
Assignment ass = GrammarUtil.containingAssignment(element);
|
||||
AbstractRule rule = element instanceof RuleCall ? ((RuleCall) element).getRule() : null;
|
||||
TypeRef type = null;
|
||||
if (ass != null || (rule instanceof ParserRule && ((ParserRule) rule).isFragment())) {
|
||||
type = GrammarUtil.containingRule(element).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 boolean isSolution(FilterState result) {
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
protected static class FilterState {
|
||||
final protected FilterState previous;
|
||||
final protected StackItem stack;
|
||||
|
@ -120,7 +180,28 @@ public class ContextTypePDAProvider implements IContextTypePDAProvider {
|
|||
}
|
||||
}
|
||||
|
||||
protected class TypeFilter implements Traverser<Pda<ISerState, RuleCall>, ISerState, FilterState> {
|
||||
protected static class TypeCollector extends AbstractTypeTraverser {
|
||||
final protected Set<EClass> types = Sets.newLinkedHashSet();
|
||||
|
||||
@Override
|
||||
protected FilterState enterType(ISerState state, FilterState previous, StackItem stack, EClass newType) {
|
||||
types.add(newType);
|
||||
return null;
|
||||
}
|
||||
|
||||
public Set<EClass> getTypes() {
|
||||
return types;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSolution(FilterState result) {
|
||||
types.add(null);
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
protected static class TypeFilter extends AbstractTypeTraverser {
|
||||
final protected EClass type;
|
||||
|
||||
public TypeFilter(EClass type) {
|
||||
|
@ -129,38 +210,15 @@ public class ContextTypePDAProvider implements IContextTypePDAProvider {
|
|||
}
|
||||
|
||||
@Override
|
||||
public FilterState enter(Pda<ISerState, RuleCall> pda, ISerState state, FilterState previous) {
|
||||
switch (state.getType()) {
|
||||
case ELEMENT:
|
||||
if (previous.type == null) {
|
||||
EClass cls = getInstantiatedType(state.getGrammarElement());
|
||||
if (cls == type)
|
||||
return new FilterState(previous, type, previous.stack, state);
|
||||
if (cls != null)
|
||||
return null;
|
||||
} else if (state.getGrammarElement() instanceof Action)
|
||||
return null;
|
||||
return new FilterState(previous, previous.type, previous.stack, state);
|
||||
case POP:
|
||||
if (previous.stack != null && state.getGrammarElement() == previous.stack.rc)
|
||||
return new FilterState(previous, previous.type, previous.stack.parent, state);
|
||||
return null;
|
||||
case PUSH:
|
||||
RuleCall rc = (RuleCall) state.getGrammarElement();
|
||||
return new FilterState(previous, previous.type, new StackItem(previous.stack, rc), state);
|
||||
case START:
|
||||
return new FilterState(previous, null, null, state);
|
||||
case STOP:
|
||||
if (previous.type == type && previous.stack == null)
|
||||
return previous;
|
||||
return null;
|
||||
}
|
||||
protected FilterState enterType(ISerState state, FilterState previous, StackItem stack, EClass newType) {
|
||||
if (newType == type)
|
||||
return new FilterState(previous, type, stack, state);
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSolution(FilterState result) {
|
||||
return true;
|
||||
return result.type == type;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -179,7 +237,7 @@ public class ContextTypePDAProvider implements IContextTypePDAProvider {
|
|||
if (getTypesForContext(grammar, context).size() > 1) {
|
||||
TypeFilter typeFilter = newTypeFilter(type);
|
||||
SerializerPDACloneFactory factory = new SerializerPDACloneFactory();
|
||||
contextTypePda = new PdaUtil().filterEdges(contextPda, typeFilter, factory);
|
||||
contextTypePda = pdaUtil.filterEdges(contextPda, typeFilter, factory);
|
||||
} else
|
||||
contextTypePda = contextPda;
|
||||
return contextTypePda;
|
||||
|
@ -194,39 +252,16 @@ public class ContextTypePDAProvider implements IContextTypePDAProvider {
|
|||
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;
|
||||
TypeCollector collector = newTypeCollector();
|
||||
pdaUtil.filterEdges(contextPda, collector, null);
|
||||
return collector.getTypes();
|
||||
}
|
||||
|
||||
protected TypeCollector newTypeCollector() {
|
||||
return new TypeCollector();
|
||||
}
|
||||
|
||||
protected TypeFilter newTypeFilter(EClass type) {
|
||||
|
|
|
@ -14,6 +14,8 @@ import org.eclipse.xtext.Action;
|
|||
import org.eclipse.xtext.Assignment;
|
||||
import org.eclipse.xtext.GrammarUtil;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
|
||||
/**
|
||||
* @author Sebastian Zarnekow - Moved from GrammarUtil
|
||||
*/
|
||||
|
@ -23,6 +25,7 @@ public class FeatureFinderUtil {
|
|||
* @since 2.0
|
||||
*/
|
||||
public static EStructuralFeature getFeature(AbstractElement grammarElement, EClass owner) {
|
||||
Preconditions.checkNotNull(owner);
|
||||
if (grammarElement == null)
|
||||
return null;
|
||||
String featureName = null;
|
||||
|
|
|
@ -393,4 +393,18 @@ public class ContextTypePDAProviderTest extends AbstractXtextTests {
|
|||
expected.append(" '}' -> stop");
|
||||
assertEquals(expected.toString(), actual);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWildcardFragment() throws Exception {
|
||||
StringBuilder grammar = new StringBuilder();
|
||||
grammar.append("Rule: F; fragment F*:name=ID;");
|
||||
String actual = getParserRule(grammar.toString());
|
||||
StringBuilder expected = new StringBuilder();
|
||||
expected.append("Rule_Rule:\n");
|
||||
expected.append(" start -> >>F\n");
|
||||
expected.append(" <<F -> stop\n");
|
||||
expected.append(" >>F -> name=ID\n");
|
||||
expected.append(" name=ID -> <<F");
|
||||
assertEquals(expected.toString(), actual);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -376,4 +376,13 @@ public class GrammarConstraintProviderTest extends AbstractXtextTests {
|
|||
expected.append(" NullRule_null returns null: {null};");
|
||||
assertEquals(expected.toString(), actual);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWildcardFragment() throws Exception {
|
||||
String actual = getParserRule("Rule: F; fragment F*:name=ID;");
|
||||
StringBuilder expected = new StringBuilder();
|
||||
expected.append("Rule: F_Rule;\n");
|
||||
expected.append(" F_Rule returns Rule: name=ID;");
|
||||
assertEquals(expected.toString(), actual);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue