[serializer] fixed bug 363509 - NPE during serialization

The problem was that some state machines referred to
EStructuralFeatures that were not member of the 
to-be-serialized EObject's EClass.
This commit is contained in:
Moritz Eysholdt 2011-11-17 17:02:19 +01:00
parent 1919e978af
commit e4f327c00b
15 changed files with 1142 additions and 373 deletions

View file

@ -19,15 +19,15 @@ import java.util.Stack;
import com.google.common.base.Function;
import com.google.common.base.Functions;
import com.google.common.base.Objects;
import com.google.common.base.Predicate;
import com.google.common.base.Predicates;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Multimap;
import com.google.common.collect.Multimaps;
import com.google.common.collect.Sets;
import com.google.inject.internal.Lists;
import com.google.inject.internal.Maps;
import com.google.inject.internal.Objects;
/**
* @author Moritz Eysholdt - Initial contribution and API
@ -75,9 +75,9 @@ public class NfaUtil {
}
public static class MappedComparator<S, COMPARABLE extends Comparable<COMPARABLE>> implements Comparator<S> {
private final Map<S, COMPARABLE> sortBy;
protected final Map<S, COMPARABLE> sortBy;
private MappedComparator(Map<S, COMPARABLE> sortBy) {
public MappedComparator(Map<S, COMPARABLE> sortBy) {
this.sortBy = sortBy;
}

View file

@ -12,7 +12,7 @@ import java.util.List;
import com.google.common.base.Function;
import com.google.common.base.Joiner;
import com.google.inject.internal.Lists;
import com.google.common.collect.Lists;
/**
* @author Moritz Eysholdt - Initial contribution and API
@ -29,6 +29,13 @@ public class PdaListFormatter<STATE, STACKITEM> implements Function<Pda<STATE, S
protected Function<? super STATE, String> stateFormatter = new ObjToStrFunction<STATE>();
protected boolean sortFollowers = false;
public PdaListFormatter<STATE, STACKITEM> sortFollowers() {
this.sortFollowers = true;
return this;
}
public String apply(Pda<STATE, STACKITEM> pda) {
return format(pda);
}
@ -73,6 +80,8 @@ public class PdaListFormatter<STATE, STACKITEM> implements Function<Pda<STATE, S
List<String> followers = Lists.newArrayList();
for (STATE f : followers2)
followers.add(title(pda, f));
if (sortFollowers)
Collections.sort(followers);
return title(pda, state) + " -> " + Joiner.on(", ").join(followers);
}

View file

@ -8,27 +8,71 @@
package org.eclipse.xtext.util.formallang;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.eclipse.xtext.util.formallang.NfaUtil.MappedComparator;
import com.google.common.base.Function;
import com.google.common.base.Functions;
import com.google.common.base.Predicate;
import com.google.common.base.Predicates;
import com.google.common.collect.LinkedHashMultimap;
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.internal.Join;
import com.google.inject.internal.Lists;
import com.google.inject.internal.Maps;
/**
* @author Moritz Eysholdt - Initial contribution and API
*/
public class PdaUtil {
public static class HashStack<T> implements Iterable<T> {
protected LinkedList<T> list = Lists.newLinkedList();
protected Set<T> set = Sets.newLinkedHashSet();
public boolean contains(Object value) {
return set.contains(value);
}
public boolean isEmpty() {
return list.isEmpty();
}
public Iterator<T> iterator() {
return list.iterator();
}
public T peek() {
return list.getLast();
}
public T pop() {
T result = list.getLast();
list.removeLast();
set.remove(result);
return result;
}
public boolean push(T value) {
list.addLast(value);
return set.add(value);
}
@Override
public String toString() {
return list.toString();
}
}
protected static class IsPop<S, P> implements Predicate<S> {
private final Pda<S, P> pda;
@ -137,6 +181,37 @@ public class PdaUtil {
}
protected static class TraversalItem<S, R> {
protected R data;
protected Iterator<S> followers;
protected S state;
public TraversalItem(S state, Iterable<S> followers, R previous) {
super();
this.state = state;
this.followers = followers.iterator();
this.data = previous;
}
@Override
@SuppressWarnings("rawtypes")
public boolean equals(Object obj) {
if (obj == null || obj.getClass() != getClass())
return false;
return data.equals(((TraversalItem) obj).data);
}
@Override
public int hashCode() {
return data.hashCode();
}
@Override
public String toString() {
return state.toString();
}
}
@Inject
protected NfaUtil nfaUtil = new NfaUtil();
@ -151,17 +226,6 @@ public class PdaUtil {
return create(cfg, ff, Functions.<E> identity(), fact);
}
public <S, P, E, T1, T2, D extends Pda<S, P>> D create(Cfg<E, T1> cfg, FollowerFunction<E> ff,
Function<E, T2> element2token, PdaFactory<D, S, P, ? super T2> fact) {
D pda = fact.create(null, null);
Map<E, S> states = Maps.newHashMap();
Map<E, S> stops = Maps.newHashMap();
Multimap<E, E> callers = new CfgUtil().getCallers(cfg);
create(cfg, pda, pda.getStart(), cfg.getRoot(), ff.getStarts(cfg.getRoot()), true, ff, element2token, fact,
states, stops, callers);
return pda;
}
protected <S, P, E, T1, T2, D extends Pda<S, P>> void create(Cfg<E, T1> cfg, D pda, S state, E ele,
Iterable<E> followerElements, boolean canEnter, FollowerFunction<E> ff, Function<E, T2> tokens,
PdaFactory<D, S, P, ? super T2> fact, Map<E, S> states, Map<E, S> stops, Multimap<E, E> callers) {
@ -203,6 +267,17 @@ public class PdaUtil {
fact.setFollowers(pda, state, followerStates);
}
public <S, P, E, T1, T2, D extends Pda<S, P>> D create(Cfg<E, T1> cfg, FollowerFunction<E> ff,
Function<E, T2> element2token, PdaFactory<D, S, P, ? super T2> fact) {
D pda = fact.create(null, null);
Map<E, S> states = Maps.newHashMap();
Map<E, S> stops = Maps.newHashMap();
Multimap<E, E> callers = new CfgUtil().getCallers(cfg);
create(cfg, pda, pda.getStart(), cfg.getRoot(), ff.getStarts(cfg.getRoot()), true, ff, element2token, fact,
states, stops, callers);
return pda;
}
protected <T> StackItem<T> createStack(Iterator<T> stack) {
if (stack.hasNext())
return new StackItem<T>(stack, stack.next());
@ -217,6 +292,67 @@ public class PdaUtil {
return UNREACHABLE;
}
public <S, P, R, D extends Pda<S, P>> D filterEdges(Pda<S, P> pda, Traverser<? super Pda<S, P>, S, R> traverser,
PdaFactory<D, S, P, S> factory) {
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());
Map<S, Integer> distances = new NfaUtil().distanceToFinalStateMap(pda);
MappedComparator<S, Integer> distanceComp = new MappedComparator<S, Integer>(distances);
trace.push(newItem(pda, distanceComp, pda.getStart(), previous));
Multimap<S, S> edges = LinkedHashMultimap.create();
HashSet<S> states = Sets.newHashSet();
HashSet<R> success = Sets.newHashSet();
states.add(pda.getStart());
states.add(pda.getStop());
ROOT: while (!trace.isEmpty()) {
TraversalItem<S, R> current = trace.peek();
while (current.followers.hasNext()) {
S next = current.followers.next();
R item = traverser.enter(pda, next, current.data);
if (item != null) {
if (next == pda.getStop() || success.contains(item)) {
S s = null;
for (TraversalItem<S, R> i : trace) {
if (s != null)
edges.put(s, i.state);
states.add(i.state);
success.add(i.data);
s = i.state;
}
edges.put(s, next);
} else {
if (trace.push(newItem(pda, distanceComp, next, item)))
continue ROOT;
}
}
}
trace.pop();
}
D result = factory.create(pda.getStart(), pda.getStop());
Map<S, S> old2new = Maps.newHashMap();
old2new.put(pda.getStart(), result.getStart());
old2new.put(pda.getStop(), result.getStop());
for (S old : states) {
if (old == pda.getStart() || old == pda.getStop())
continue;
else if (pda.getPop(old) != null)
old2new.put(old, factory.createPop(result, old));
else if (pda.getPush(old) != null)
old2new.put(old, factory.createPush(result, old));
else
old2new.put(old, factory.createState(result, old));
}
for (S old : states) {
List<S> followers = Lists.newArrayList();
for (S f : edges.get(old))
followers.add(old2new.get(f));
factory.setFollowers(result, old2new.get(old), followers);
}
return result;
}
public <S, P> Nfa<S> filterUnambiguousPaths(Pda<S, P> pda) {
Map<S, List<S>> followers = Maps.newHashMap();
Map<S, Integer> distanceMap = nfaUtil.distanceToFinalStateMap(pda);
@ -260,6 +396,12 @@ public class PdaUtil {
filterUnambiguousPaths(pda, follower, dist, followers);
}
protected <S, R, P> TraversalItem<S, R> newItem(Pda<S, P> pda, MappedComparator<S, Integer> comp, S next, R item) {
List<S> followers = Lists.newArrayList(pda.getFollowers(next));
Collections.sort(followers, comp);
return new TraversalItem<S, R>(next, followers, item);
}
public <S, P> List<S> shortestPathTo(Pda<S, P> pda, Iterable<S> starts, Iterator<P> stack, Predicate<S> matches,
Predicate<S> canPass) {
TraceItem<S, P> trace = trace(pda, starts, stack, matches, canPass);

View file

@ -0,0 +1,15 @@
/*******************************************************************************
* 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.util.formallang;
/**
* @author Moritz Eysholdt - Initial contribution and API
*/
public interface Traverser<G extends DirectedGraph<S>, S, R> {
public R enter(G graph, S state, R previous);
}

View file

@ -0,0 +1,129 @@
/*******************************************************************************
* 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.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.GrammarUtil;
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.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.collect.Maps;
import com.google.common.collect.Sets;
import com.google.inject.Singleton;
/**
* @author Moritz Eysholdt - Initial contribution and API
*/
@Singleton
public class ContextPDAProvider implements IContextPDAProvider {
protected static class SerializerCfg extends CfgAdapter {
protected EObject context;
public SerializerCfg(EObject context) {
super(GrammarUtil.getGrammar(context));
this.context = context;
}
@Override
public AbstractElement getCall(AbstractElement ele) {
if (ele instanceof RuleCall && !GrammarUtil.isAssigned(ele)
&& ((RuleCall) ele).getRule().getType().getClassifier() instanceof EClass)
return ((RuleCall) ele).getRule().getAlternatives();
return null;
}
@Override
public AbstractElement getRoot() {
if (context instanceof AbstractRule)
return ((AbstractRule) context).getAlternatives();
if (context instanceof Action)
return GrammarUtil.containingRule(context).getAlternatives();
return super.getRoot();
}
}
protected static class SerializerFollowerFunction extends FollowerFunctionImpl<AbstractElement, AbstractElement> {
protected Action actionCtx;
protected AbstractRule ruleCtx;
public SerializerFollowerFunction(Production<AbstractElement, AbstractElement> production, EObject context) {
super(production);
this.actionCtx = context instanceof Action ? (Action) context : null;
this.ruleCtx = context instanceof AbstractRule ? (AbstractRule) context : null;
}
@Override
public Iterable<AbstractElement> getFollowers(AbstractElement element) {
Set<AbstractElement> result = Sets.newLinkedHashSet();
for (AbstractElement ele : super.getFollowers(element))
if (ele == null) {
if (isStop(element))
result.add(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) {
if (isStop(root))
result.add(null);
} else if (actionCtx == ele) {
result.add(null);
} else if (!GrammarUtil.isAssignedAction(ele))
result.add(ele);
return result;
}
protected boolean isStop(AbstractElement element) {
return actionCtx == null || (GrammarUtil.containingRule(actionCtx) != GrammarUtil.containingRule(element));
}
}
protected Map<EObject, Pda<ISerState, RuleCall>> cache = Maps.newHashMap();
protected Pda<ISerState, RuleCall> createPDA(EObject context) {
SerializerCfg cfg = new SerializerCfg(context);
SerializerFollowerFunction ff = new SerializerFollowerFunction(cfg, context);
Pda<ISerState, RuleCall> pda = new PdaUtil().create(cfg, ff, new SerializerPDAElementFactory());
new NfaUtil().removeOrphans(pda);
return pda;
}
public Pda<ISerState, RuleCall> getContextPDA(EObject context) {
Pda<ISerState, RuleCall> result = cache.get(context);
if (result == null)
cache.put(context, result = createPDA(context));
return result;
}
}

View file

@ -0,0 +1,191 @@
/*******************************************************************************
* 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 org.eclipse.emf.ecore.EClass;
import org.eclipse.emf.ecore.EClassifier;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.xtext.Action;
import org.eclipse.xtext.Assignment;
import org.eclipse.xtext.GrammarUtil;
import org.eclipse.xtext.RuleCall;
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.formallang.Pda;
import org.eclipse.xtext.util.formallang.PdaUtil;
import org.eclipse.xtext.util.formallang.Traverser;
import com.google.common.collect.Maps;
import com.google.inject.Inject;
import com.google.inject.Singleton;
/**
* @author Moritz Eysholdt - Initial contribution and API
*/
@Singleton
public class ContextTypePDAProvider implements IContextTypePDAProvider {
protected static class FilterState {
final protected FilterState previous;
final protected StackItem stack;
final protected ISerState state;
final protected EClass type;
public FilterState(FilterState previous, EClass type, StackItem stack, ISerState state) {
super();
this.previous = previous;
this.type = type;
this.stack = stack;
this.state = state;
}
@Override
public boolean equals(Object obj) {
if (obj == null || obj.getClass() != getClass())
return false;
FilterState other = (FilterState) obj;
if (type != other.type || state != other.state)
return false;
if (stack == null)
return other.stack == null;
return stack.equals(other.stack);
}
@Override
public int hashCode() {
int r = state.getType().hashCode();
if (state.getGrammarElement() != null)
r *= state.getGrammarElement().hashCode();
if (type != null)
r *= type.hashCode() * 7;
if (stack != null)
r *= stack.rc.hashCode() * 13;
return r;
}
}
protected static class StackItem {
final protected StackItem parent;
final protected RuleCall rc;
public StackItem(StackItem parent, RuleCall rc) {
super();
this.parent = parent;
this.rc = rc;
}
@Override
public boolean equals(Object obj) {
if (obj == null || obj.getClass() != getClass())
return false;
StackItem current1 = this;
StackItem current2 = (StackItem) obj;
int count = 0;
while (true) {
if (current1 == null && current2 == null)
return true;
if (current1 == null || current2 == null)
return false;
if (current1.rc != current2.rc)
return false;
if (current1 != this && current1.rc == this.rc)
count++;
if (count > 0)
return true;
current1 = current1.parent;
current2 = current2.parent;
}
}
@Override
public int hashCode() {
return rc.hashCode();
}
}
protected static class TypeFilter implements Traverser<Pda<ISerState, RuleCall>, ISerState, FilterState> {
final protected EClass type;
public TypeFilter(EClass type) {
super();
this.type = type;
}
public FilterState enter(Pda<ISerState, RuleCall> pda, ISerState state, FilterState previous) {
switch (state.getType()) {
case ELEMENT:
if (previous.type == null) {
Assignment ass = GrammarUtil.containingAssignment(state.getGrammarElement());
if (ass != null) {
EClassifier cls = GrammarUtil.containingRule(ass).getType().getClassifier();
if (cls == type)
return new FilterState(previous, type, previous.stack, state);
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);
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;
}
return null;
}
}
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);
Pda<ISerState, RuleCall> contextTypePda = null;
if (contextProvider.getTypesForContext(context).size() > 1) {
TypeFilter typeFilter = newTypeFilter(type);
SerializerPDACloneFactory factory = new SerializerPDACloneFactory();
contextTypePda = new PdaUtil().filterEdges(contextPda, typeFilter, factory);
} else
contextTypePda = contextPda;
return contextTypePda;
}
public Pda<ISerState, RuleCall> getContextTypePDA(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));
return result;
}
protected TypeFilter newTypeFilter(EClass type) {
return new TypeFilter(type);
}
}

View file

@ -0,0 +1,23 @@
/*******************************************************************************
* 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 org.eclipse.emf.ecore.EObject;
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(ContextPDAProvider.class)
public interface IContextPDAProvider {
Pda<ISerState, RuleCall> getContextPDA(EObject context);
}

View file

@ -7,11 +7,8 @@
*******************************************************************************/
package org.eclipse.xtext.serializer.analysis;
import java.util.List;
import org.eclipse.emf.ecore.EClass;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.xtext.AbstractElement;
import org.eclipse.xtext.RuleCall;
import org.eclipse.xtext.util.formallang.Pda;
@ -20,20 +17,8 @@ import com.google.inject.ImplementedBy;
/**
* @author Moritz Eysholdt - Initial contribution and API
*/
@ImplementedBy(SerializerPDAProvider.class)
public interface ISerializerPDAProvider {
@ImplementedBy(ContextTypePDAProvider.class)
public interface IContextTypePDAProvider {
public interface ISerState {
List<? extends ISerState> getFollowers();
AbstractElement getGrammarElement();
SerStateType getType();
}
public enum SerStateType {
ELEMENT, POP, PUSH, START, STOP;
}
Pda<? extends ISerState, RuleCall> getPDA(EObject context, EClass type);
Pda<ISerState, RuleCall> getContextTypePDA(EObject context, EClass type);
}

View file

@ -0,0 +1,24 @@
/*******************************************************************************
* 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.List;
import org.eclipse.xtext.AbstractElement;
public interface ISerState {
public enum SerStateType {
ELEMENT, POP, PUSH, START, STOP;
}
List<? extends ISerState> getFollowers();
AbstractElement getGrammarElement();
SerStateType getType();
}

View file

@ -0,0 +1,186 @@
/*******************************************************************************
* Copyright (c) 2011 itemis AG (http://www.itemis.eu) and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*******************************************************************************/
package org.eclipse.xtext.serializer.analysis;
import java.util.Collections;
import java.util.List;
import org.eclipse.xtext.AbstractElement;
import org.eclipse.xtext.RuleCall;
import org.eclipse.xtext.grammaranalysis.impl.GrammarElementTitleSwitch;
import org.eclipse.xtext.serializer.analysis.ISerState.SerStateType;
import org.eclipse.xtext.util.formallang.NfaGraphFormatter;
import org.eclipse.xtext.util.formallang.NfaUtil;
import org.eclipse.xtext.util.formallang.Pda;
import org.eclipse.xtext.util.formallang.PdaFactory;
import com.google.common.collect.Lists;
public class SerializerPDA implements Pda<ISerState, RuleCall> {
public static class SerializerPDACloneFactory implements PdaFactory<SerializerPDA, ISerState, RuleCall, ISerState> {
public SerializerPDA create(ISerState start, ISerState stop) {
SerializerPDA.SerializerPDAState s1 = new SerializerPDAState(start.getGrammarElement(), SerStateType.START);
SerializerPDA.SerializerPDAState s2 = new SerializerPDAState(stop.getGrammarElement(), SerStateType.STOP);
return new SerializerPDA(s1, s2);
}
public ISerState createPop(SerializerPDA pda, ISerState token) {
return new SerializerPDAState(token.getGrammarElement(), SerStateType.POP);
}
public ISerState createPush(SerializerPDA pda, ISerState token) {
return new SerializerPDAState(token.getGrammarElement(), SerStateType.PUSH);
}
public ISerState createState(SerializerPDA nfa, ISerState token) {
return new SerializerPDAState(token.getGrammarElement(), SerStateType.ELEMENT);
}
public void setFollowers(SerializerPDA nfa, ISerState owner, Iterable<ISerState> followers) {
((SerializerPDA.SerializerPDAState) owner).followers = Lists.newArrayList(followers);
}
}
public static class SerializerPDAElementFactory implements
PdaFactory<SerializerPDA, ISerState, RuleCall, AbstractElement> {
public SerializerPDA create(AbstractElement start, AbstractElement stop) {
SerializerPDA.SerializerPDAState s1 = new SerializerPDAState(start, SerStateType.START);
SerializerPDA.SerializerPDAState s2 = new SerializerPDAState(stop, SerStateType.STOP);
return new SerializerPDA(s1, s2);
}
public ISerState createPop(SerializerPDA pda, AbstractElement token) {
return new SerializerPDAState(token, SerStateType.POP);
}
public ISerState createPush(SerializerPDA pda, AbstractElement token) {
return new SerializerPDAState(token, SerStateType.PUSH);
}
public ISerState createState(SerializerPDA nfa, AbstractElement token) {
return new SerializerPDAState(token, SerStateType.ELEMENT);
}
public void setFollowers(SerializerPDA nfa, ISerState owner, Iterable<ISerState> followers) {
((SerializerPDA.SerializerPDAState) owner).followers = Lists.newArrayList(followers);
}
}
protected static class SerializerPDAState implements ISerState {
protected List<ISerState> followers = Collections.emptyList();
protected AbstractElement grammarElement;
protected SerStateType type;
public SerializerPDAState(AbstractElement grammarElement, SerStateType type) {
super();
this.type = type;
this.grammarElement = grammarElement;
}
@Override
public boolean equals(Object obj) {
if (obj == null || obj.getClass() != getClass())
return false;
SerializerPDA.SerializerPDAState s = (SerializerPDA.SerializerPDAState) obj;
return grammarElement == s.grammarElement && type == s.type;
}
public List<ISerState> getFollowers() {
return followers;
}
public AbstractElement getGrammarElement() {
return grammarElement;
}
public SerStateType getType() {
return type;
}
@Override
public int hashCode() {
return (grammarElement != null ? grammarElement.hashCode() : 1) * type.hashCode();
}
@Override
public String toString() {
GrammarElementTitleSwitch fmt = new GrammarElementTitleSwitch().hideCardinality().showQualified()
.showAssignments();
switch (type) {
case ELEMENT:
return fmt.apply(grammarElement);
case POP:
return "<<" + fmt.apply(grammarElement);
case PUSH:
return ">>" + fmt.apply(grammarElement);
case START:
return "start";
case STOP:
return "stop";
}
return "";
}
}
protected SerializerPDA.SerializerPDAState start;
protected SerializerPDA.SerializerPDAState stop;
public SerializerPDA(SerializerPDA.SerializerPDAState start, SerializerPDA.SerializerPDAState stop) {
super();
this.start = start;
this.stop = stop;
}
@Override
public boolean equals(Object obj) {
if (obj == null || obj.getClass() != getClass())
return false;
return new NfaUtil().equalsIgnoreOrder(this, (SerializerPDA) obj);
}
public Iterable<ISerState> getFollowers(ISerState state) {
return ((SerializerPDA.SerializerPDAState) state).followers;
}
public RuleCall getPop(ISerState state) {
SerializerPDA.SerializerPDAState s = (SerializerPDA.SerializerPDAState) state;
return s.type == SerStateType.POP ? (RuleCall) s.grammarElement : null;
}
public RuleCall getPush(ISerState state) {
SerializerPDA.SerializerPDAState s = (SerializerPDA.SerializerPDAState) state;
return s.type == SerStateType.PUSH ? (RuleCall) s.grammarElement : null;
}
public ISerState getStart() {
return start;
}
public ISerState getStop() {
return stop;
}
@Override
public int hashCode() {
int r = 0;
if (start != null && start.followers != null)
for (ISerState s : start.followers)
if (s != null)
r += s.hashCode();
return r;
}
@Override
public String toString() {
return new NfaGraphFormatter().format(this);
}
}

View file

@ -1,303 +0,0 @@
/*******************************************************************************
* Copyright (c) 2011 itemis AG (http://www.itemis.eu) and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*******************************************************************************/
package org.eclipse.xtext.serializer.analysis;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.eclipse.emf.ecore.EClass;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.xtext.AbstractElement;
import org.eclipse.xtext.AbstractRule;
import org.eclipse.xtext.Action;
import org.eclipse.xtext.GrammarUtil;
import org.eclipse.xtext.RuleCall;
import org.eclipse.xtext.grammaranalysis.impl.CfgAdapter;
import org.eclipse.xtext.grammaranalysis.impl.GrammarElementTitleSwitch;
import org.eclipse.xtext.util.Pair;
import org.eclipse.xtext.util.Tuples;
import org.eclipse.xtext.util.formallang.FollowerFunctionImpl;
import org.eclipse.xtext.util.formallang.NfaGraphFormatter;
import org.eclipse.xtext.util.formallang.NfaUtil;
import org.eclipse.xtext.util.formallang.Pda;
import org.eclipse.xtext.util.formallang.PdaFactory;
import org.eclipse.xtext.util.formallang.PdaUtil;
import org.eclipse.xtext.util.formallang.Production;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import com.google.inject.Singleton;
/**
* @author Moritz Eysholdt - Initial contribution and API
*/
@Singleton
public class SerializerPDAProvider implements ISerializerPDAProvider {
protected static class SerializerCfg extends CfgAdapter {
protected EObject context;
public SerializerCfg(EObject context) {
super(GrammarUtil.getGrammar(context));
this.context = context;
}
@Override
public AbstractElement getRoot() {
if (context instanceof AbstractRule)
return ((AbstractRule) context).getAlternatives();
if (context instanceof Action)
return GrammarUtil.containingRule(context).getAlternatives();
return super.getRoot();
}
@Override
public AbstractElement getCall(AbstractElement ele) {
if (ele instanceof RuleCall && !GrammarUtil.isAssigned(ele)
&& ((RuleCall) ele).getRule().getType().getClassifier() instanceof EClass)
return ((RuleCall) ele).getRule().getAlternatives();
return null;
}
}
protected static class SerializerFollowerFunction extends FollowerFunctionImpl<AbstractElement, AbstractElement> {
protected Action actionCtx;
protected AbstractRule ruleCtx;
protected EClass type;
public SerializerFollowerFunction(Production<AbstractElement, AbstractElement> production, EObject context,
EClass type) {
super(production);
this.actionCtx = context instanceof Action ? (Action) context : null;
this.ruleCtx = context instanceof AbstractRule ? (AbstractRule) context : null;
this.type = type;
}
@Override
public Iterable<AbstractElement> getFollowers(AbstractElement element) {
Set<AbstractElement> result = Sets.newLinkedHashSet();
for (AbstractElement ele : super.getFollowers(element))
if (ele == null) {
if (actionCtx == null
|| (GrammarUtil.containingRule(actionCtx) != GrammarUtil.containingRule(element)))
result.add(null);
} else if (actionCtx == ele)
result.add(null);
else if (ele instanceof Action) {
Action a = (Action) ele;
if (type != null && a.getFeature() == null && a.getType().getClassifier() == type)
result.add(ele);
} else if (type != null || !GrammarUtil.isAssigned(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 (type != null && act.getFeature() != null && act.getType().getClassifier() == type)
result.add(act);
for (AbstractElement ele : super.getStarts(root))
if (ele == null) {
if (actionCtx == null
|| (GrammarUtil.containingRule(actionCtx) != GrammarUtil.containingRule(root)))
result.add(null);
} else if (actionCtx == ele) {
result.add(null);
} else if (!GrammarUtil.isAssignedAction(ele)
&& typeMatches(ele, Sets.<AbstractElement> newHashSet()) != Boolean.FALSE)
result.add(ele);
return result;
}
protected Boolean typeMatches(AbstractElement ele, Set<AbstractElement> visited) {
if (!visited.add(ele))
return null;
if (ele instanceof Action)
return type != null && ((Action) ele).getType().getClassifier() == type;
if (GrammarUtil.isAssigned(ele))
return type != null && GrammarUtil.containingRule(ele).getType().getClassifier() == type;
else if (GrammarUtil.isEObjectRuleCall(ele))
for (Action act : GrammarUtil.containedActions(((RuleCall) ele).getRule().getAlternatives()))
if (act.getFeature() != null && act.getType().getClassifier() == type)
return true;
boolean allFalse = true;
for (AbstractElement f : GrammarUtil.isEObjectRuleCall(ele) && !GrammarUtil.isAssigned(ele) ? super
.getStarts(((RuleCall) ele).getRule().getAlternatives()) : super.getFollowers(ele))
if (f != null) {
Boolean r = typeMatches(f, visited);
if (r == Boolean.TRUE)
return true;
if (r == null)
allFalse = false;
} else if (type == null)
allFalse = false;
return allFalse ? false : null;
}
}
protected static class SerializerPDA implements Pda<SerializerPDAState, RuleCall> {
protected SerializerPDAState start;
protected SerializerPDAState stop;
public SerializerPDA(SerializerPDAState start, SerializerPDAState stop) {
super();
this.start = start;
this.stop = stop;
}
@Override
public boolean equals(Object obj) {
if (obj == null || obj.getClass() != getClass())
return false;
return new NfaUtil().equalsIgnoreOrder(this, (SerializerPDA) obj);
}
public Iterable<SerializerPDAState> getFollowers(SerializerPDAState state) {
return state.followers;
}
public RuleCall getPop(SerializerPDAState state) {
return state.type == SerStateType.POP ? (RuleCall) state.grammarElement : null;
}
public RuleCall getPush(SerializerPDAState state) {
return state.type == SerStateType.PUSH ? (RuleCall) state.grammarElement : null;
}
public SerializerPDAState getStart() {
return start;
}
public SerializerPDAState getStop() {
return stop;
}
@Override
public int hashCode() {
int r = 0;
if (start != null && start.followers != null)
for (SerializerPDAState s : start.followers)
if (s != null)
r += s.hashCode();
return r;
}
@Override
public String toString() {
return new NfaGraphFormatter().format(this);
}
}
protected static class SerializerPDAFactory implements
PdaFactory<SerializerPDA, SerializerPDAState, RuleCall, AbstractElement> {
public SerializerPDA create(AbstractElement start, AbstractElement stop) {
SerializerPDAState s1 = new SerializerPDAState(start, SerStateType.START);
SerializerPDAState s2 = new SerializerPDAState(stop, SerStateType.STOP);
return new SerializerPDA(s1, s2);
}
public SerializerPDAState createPop(SerializerPDA pda, AbstractElement token) {
return new SerializerPDAState(token, SerStateType.POP);
}
public SerializerPDAState createPush(SerializerPDA pda, AbstractElement token) {
return new SerializerPDAState(token, SerStateType.PUSH);
}
public SerializerPDAState createState(SerializerPDA nfa, AbstractElement token) {
return new SerializerPDAState(token, SerStateType.ELEMENT);
}
public void setFollowers(SerializerPDA nfa, SerializerPDAState owner, Iterable<SerializerPDAState> followers) {
owner.followers = Lists.newArrayList(followers);
}
}
protected static class SerializerPDAState implements ISerState {
protected List<SerializerPDAState> followers = Collections.emptyList();
protected AbstractElement grammarElement;
protected SerStateType type;
public SerializerPDAState(AbstractElement grammarElement, SerStateType type) {
super();
this.type = type;
this.grammarElement = grammarElement;
}
@Override
public boolean equals(Object obj) {
if (obj == null || obj.getClass() != getClass())
return false;
SerializerPDAState s = (SerializerPDAState) obj;
return grammarElement == s.grammarElement && type == s.type;
}
public List<SerializerPDAState> getFollowers() {
return followers;
}
public AbstractElement getGrammarElement() {
return grammarElement;
}
public SerStateType getType() {
return type;
}
@Override
public int hashCode() {
return (grammarElement != null ? grammarElement.hashCode() : 1) * type.hashCode();
}
@Override
public String toString() {
GrammarElementTitleSwitch fmt = new GrammarElementTitleSwitch().hideCardinality().showQualified();
switch (type) {
case ELEMENT:
return fmt.apply(grammarElement);
case POP:
return "<<" + fmt.apply(grammarElement);
case PUSH:
return ">>" + fmt.apply(grammarElement);
case START:
return "start";
case STOP:
return "stop";
}
return "";
}
}
protected Map<Pair<EObject, EClass>, Pda<? extends ISerState, RuleCall>> cache = Maps.newHashMap();
protected Pda<? extends ISerState, RuleCall> createPDA(EObject context, EClass type) {
SerializerCfg cfg = new SerializerCfg(context);
SerializerFollowerFunction ff = new SerializerFollowerFunction(cfg, context, type);
Pda<? extends ISerState, RuleCall> pda = new PdaUtil().create(cfg, ff, new SerializerPDAFactory());
new NfaUtil().removeOrphans(pda);
return pda;
}
public Pda<? extends ISerState, RuleCall> getPDA(EObject context, EClass type) {
Pair<EObject, EClass> key = Tuples.create(context, type);
Pda<? extends ISerState, RuleCall> result = cache.get(key);
if (result == null)
cache.put(key, result = createPDA(context, type));
return result;
}
}

View file

@ -31,8 +31,7 @@ import org.eclipse.xtext.serializer.analysis.GrammarAlias.AlternativeAlias;
import org.eclipse.xtext.serializer.analysis.GrammarAlias.GrammarAliasFactory;
import org.eclipse.xtext.serializer.analysis.GrammarAlias.GroupAlias;
import org.eclipse.xtext.serializer.analysis.GrammarAlias.TokenAlias;
import org.eclipse.xtext.serializer.analysis.ISerializerPDAProvider.ISerState;
import org.eclipse.xtext.serializer.analysis.ISerializerPDAProvider.SerStateType;
import org.eclipse.xtext.serializer.analysis.ISerState.SerStateType;
import org.eclipse.xtext.serializer.sequencer.RuleCallStack;
import org.eclipse.xtext.util.Pair;
import org.eclipse.xtext.util.Tuples;
@ -505,7 +504,7 @@ public class SyntacticSequencerPDAProvider implements ISyntacticSequencerPDAProv
// protected SequencerPDAProvider pdaProvider = createSequencerPDAProvider();
@Inject
protected SerializerPDAProvider pdaProvider;
protected ContextTypePDAProvider pdaProvider;
protected boolean canReachAbsorber(ISerState from, ISerState to, Set<ISerState> visited) {
if (isMandatoryAbsorber(from.getGrammarElement()) || !visited.add(from))
@ -602,7 +601,7 @@ 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.getPDA(context, type);
Pda<? extends ISerState, RuleCall> pda = pdaProvider.getContextTypePDA(context, type);
result = createAbsorberState(pda.getStart(), absorbers, emitters, context, type);
cache.put(key, result);
}

View file

@ -0,0 +1,287 @@
/*******************************************************************************
* 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;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.xtext.AbstractElement;
import org.eclipse.xtext.Grammar;
import org.eclipse.xtext.RuleCall;
import org.eclipse.xtext.XtextStandaloneSetup;
import org.eclipse.xtext.grammaranalysis.impl.GrammarElementTitleSwitch;
import org.eclipse.xtext.junit.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 com.google.common.base.Function;
import com.google.common.collect.Lists;
import com.google.inject.internal.Join;
/**
* @author Moritz Eysholdt - Initial contribution and API
*/
public class ContextPDAProviderTest extends AbstractXtextTests {
private static class ToStr implements Function<ISerState, String> {
private Function<AbstractElement, String> ts = new GrammarElementTitleSwitch().showAssignments()
.hideCardinality().showQualified();
public String apply(ISerState from) {
switch (from.getType()) {
case START:
return "start";
case STOP:
return "stop";
default:
return ts.apply(from.getGrammarElement());
}
}
}
final static String HEADER = "grammar org.eclipse.xtext.serializer.SequenceParserPDAProviderTestLanguage"
+ " with org.eclipse.xtext.common.Terminals "
+ "generate sequenceParserPDAProviderTest \"http://www.eclipse.org/2010/tmf/xtext/SequenceParserPDAProvider\" ";
private List<Pair<EObject, String>> getContexts(Grammar grammar) {
final Context2NameFunction ctx2name = get(Context2NameFunction.class);
final IContextProvider contextProvider = get(IContextProvider.class);
List<Pair<EObject, String>> result = Lists.newArrayList();
for (EObject ctx : contextProvider.getAllContexts(grammar))
result.add(Tuples.create(ctx, ctx2name.getContextName(ctx)));
Collections.sort(result, new Comparator<Pair<EObject, String>>() {
public int compare(Pair<EObject, String> o1, Pair<EObject, String> o2) {
return o1.getSecond().compareTo(o2.getSecond());
}
});
return result;
}
protected String getParserRule(String body) throws Exception {
Grammar grammar = (Grammar) getModel(HEADER + body);
List<String> result = Lists.newArrayList();
PdaListFormatter<ISerState, RuleCall> formatter = new PdaListFormatter<ISerState, RuleCall>();
formatter.setStateFormatter(new ToStr());
formatter.setStackitemFormatter(new GrammarElementTitleSwitch().showAssignments().hideCardinality());
for (Pair<EObject, String> ctx : getContexts(grammar)) {
result.add(ctx.getSecond() + ":");
Pda<ISerState, RuleCall> pda = get(IContextPDAProvider.class).getContextPDA(ctx.getFirst());
result.add(" " + formatter.format(pda).replace("\n", "\n "));
}
return Join.join("\n", result);
}
@Override
protected void setUp() throws Exception {
super.setUp();
with(XtextStandaloneSetup.class);
}
public void testKeywordAlternative() throws Exception {
String actual = getParserRule("Rule: a1=ID ('kw1' | 'kw2') a2=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");
assertEquals(expected.toString(), actual);
}
public void testDelegation1() throws Exception {
String actual = getParserRule("Rule: Delegate; Delegate: val=ID;");
StringBuilder expected = new StringBuilder();
expected.append("Delegate:\n");
expected.append(" start -> val=ID\n");
expected.append(" val=ID -> stop\n");
expected.append("Rule:\n");
expected.append(" start -> >>Delegate\n");
expected.append(" <<Delegate -> stop\n");
expected.append(" >>Delegate -> val=ID\n");
expected.append(" val=ID -> <<Delegate");
assertEquals(expected.toString(), actual);
}
public void testDelegation2() throws Exception {
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");
expected.append(" 'del' -> >>Delegate2\n");
expected.append(" <<Delegate2 -> bar=ID\n");
expected.append(" >>Delegate2 -> val=ID\n");
expected.append(" bar=ID -> stop\n");
expected.append(" val=ID -> <<Delegate2\n");
expected.append("Delegate2:\n");
expected.append(" start -> val=ID\n");
expected.append(" val=ID -> stop\n");
expected.append("Foo:\n");
expected.append(" start -> val2=ID\n");
expected.append(" val2=ID -> stop\n");
expected.append("Rule:\n");
expected.append(" start -> >>Foo, >>Delegate1\n");
expected.append(" 'del' -> >>Delegate2\n");
expected.append(" <<Delegate1 -> stop\n");
expected.append(" <<Delegate2 -> bar=ID\n");
expected.append(" <<Foo -> stop\n");
expected.append(" >>Delegate1 -> 'del'\n");
expected.append(" >>Delegate2 -> val=ID\n");
expected.append(" >>Foo -> val2=ID\n");
expected.append(" bar=ID -> <<Delegate1\n");
expected.append(" val2=ID -> <<Foo\n");
expected.append(" val=ID -> <<Delegate2");
assertEquals(expected.toString(), actual);
}
public void testActionMandatory() throws Exception {
String actual = getParserRule("Rule: val1=ID {Act.val2=current} val3=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");
assertEquals(expected.toString(), actual);
}
public void testActionOptional() throws Exception {
String actual = getParserRule("Rule: val1=ID ({Act.val2=current} val3=ID)?;");
StringBuilder expected = new StringBuilder();
expected.append("Rule:\n");
expected.append(" start -> {Act.val2=}, val1=ID\n");
expected.append(" val1=ID -> stop\n");
expected.append(" val3=ID -> stop\n");
expected.append(" {Act.val2=} -> val3=ID\n");
expected.append("Rule_Act_1_0:\n");
expected.append(" start -> val1=ID\n");
expected.append(" val1=ID -> stop");
assertEquals(expected.toString(), actual);
}
public void testActionManyMandatory() throws Exception {
String actual = getParserRule("Rule: val1=ID ({Act.val2=current} val3=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_0:\n");
expected.append(" start -> {Act.val2=}, val1=ID\n");
expected.append(" val1=ID -> stop\n");
expected.append(" val3=ID -> stop\n");
expected.append(" {Act.val2=} -> val3=ID");
assertEquals(expected.toString(), actual);
}
public void testActionManyOptional() throws Exception {
String actual = getParserRule("Rule: val1=ID ({Act.val2=current} val3=ID)*;");
StringBuilder expected = new StringBuilder();
expected.append("Rule:\n");
expected.append(" start -> {Act.val2=}, val1=ID\n");
expected.append(" val1=ID -> stop\n");
expected.append(" val3=ID -> stop\n");
expected.append(" {Act.val2=} -> val3=ID\n");
expected.append("Rule_Act_1_0:\n");
expected.append(" start -> {Act.val2=}, val1=ID\n");
expected.append(" val1=ID -> stop\n");
expected.append(" val3=ID -> stop\n");
expected.append(" {Act.val2=} -> val3=ID");
assertEquals(expected.toString(), actual);
}
public void testActionTwoMandatory() throws Exception {
String actual = getParserRule("Rule: val1=ID {Act1.val2=current} val3=ID {Act2.val2=current} val4=ID;");
StringBuilder expected = new StringBuilder();
expected.append("Rule:\n");
expected.append(" start -> {Act2.val2=}\n");
expected.append(" val4=ID -> stop\n");
expected.append(" {Act2.val2=} -> val4=ID\n");
expected.append("Rule_Act1_1:\n");
expected.append(" start -> val1=ID\n");
expected.append(" val1=ID -> stop\n");
expected.append("Rule_Act2_3:\n");
expected.append(" start -> {Act1.val2=}\n");
expected.append(" val3=ID -> stop\n");
expected.append(" {Act1.val2=} -> val3=ID");
assertEquals(expected.toString(), actual);
}
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;");
StringBuilder expected = new StringBuilder();
expected.append("Addit:\n");
expected.append(" start -> {Add.left=}, >>Prim\n");
expected.append(" '+' -> right=Prim\n");
expected.append(" <<Prim -> stop\n");
expected.append(" >>Prim -> {Val}\n");
expected.append(" right=Prim -> stop\n");
expected.append(" val=ID -> <<Prim\n");
expected.append(" {Add.left=} -> '+'\n");
expected.append(" {Val} -> val=ID\n");
expected.append("Addit_Add_1_0:\n");
expected.append(" start -> {Add.left=}, >>Prim\n");
expected.append(" '+' -> right=Prim\n");
expected.append(" <<Prim -> stop\n");
expected.append(" >>Prim -> {Val}\n");
expected.append(" right=Prim -> stop\n");
expected.append(" val=ID -> <<Prim\n");
expected.append(" {Add.left=} -> '+'\n");
expected.append(" {Val} -> val=ID\n");
expected.append("Exp:\n");
expected.append(" start -> 'kw'\n");
expected.append(" '+' -> right=Prim\n");
expected.append(" 'kw' -> >>Addit\n");
expected.append(" <<Addit -> stop\n");
expected.append(" <<Prim -> <<Addit\n");
expected.append(" >>Addit -> {Add.left=}, >>Prim\n");
expected.append(" >>Prim -> {Val}\n");
expected.append(" right=Prim -> <<Addit\n");
expected.append(" val=ID -> <<Prim\n");
expected.append(" {Add.left=} -> '+'\n");
expected.append(" {Val} -> val=ID\n");
expected.append("Prim:\n");
expected.append(" start -> {Val}\n");
expected.append(" val=ID -> stop\n");
expected.append(" {Val} -> val=ID");
assertEquals(expected.toString(), actual);
}
public void testOptionalDelegate() throws Exception {
String actual = getParserRule("Rule: Mand | Opt; Mand: 'm' mand=ID; Opt: 'o' opt=ID?;");
StringBuilder expected = new StringBuilder();
expected.append("Mand:\n");
expected.append(" start -> 'm'\n");
expected.append(" 'm' -> mand=ID\n");
expected.append(" mand=ID -> stop\n");
expected.append("Opt:\n");
expected.append(" start -> 'o'\n");
expected.append(" 'o' -> opt=ID, stop\n");
expected.append(" opt=ID -> stop\n");
expected.append("Rule:\n");
expected.append(" start -> >>Mand, >>Opt\n");
expected.append(" 'm' -> mand=ID\n");
expected.append(" 'o' -> opt=ID, <<Opt\n");
expected.append(" <<Mand -> stop\n");
expected.append(" <<Opt -> stop\n");
expected.append(" >>Mand -> 'm'\n");
expected.append(" >>Opt -> 'o'\n");
expected.append(" mand=ID -> <<Mand\n");
expected.append(" opt=ID -> <<Opt");
assertEquals(expected.toString(), actual);
}
}

View file

@ -7,7 +7,6 @@
*******************************************************************************/
package org.eclipse.xtext.serializer;
import java.io.IOException;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
@ -18,18 +17,16 @@ import org.eclipse.xtext.AbstractElement;
import org.eclipse.xtext.Grammar;
import org.eclipse.xtext.RuleCall;
import org.eclipse.xtext.XtextStandaloneSetup;
import org.eclipse.xtext.generator.serializer.SyntacticSequencerPDA2ExtendedDot;
import org.eclipse.xtext.grammaranalysis.impl.GrammarElementTitleSwitch;
import org.eclipse.xtext.junit.AbstractXtextTests;
import org.eclipse.xtext.serializer.analysis.Context2NameFunction;
import org.eclipse.xtext.serializer.analysis.IContextProvider;
import org.eclipse.xtext.serializer.analysis.ISerializerPDAProvider;
import org.eclipse.xtext.serializer.analysis.ISerializerPDAProvider.ISerState;
import org.eclipse.xtext.serializer.analysis.ISerState;
import org.eclipse.xtext.serializer.analysis.IContextTypePDAProvider;
import org.eclipse.xtext.util.Triple;
import org.eclipse.xtext.util.Tuples;
import org.eclipse.xtext.util.formallang.Pda;
import org.eclipse.xtext.util.formallang.PdaListFormatter;
import org.eclipse.xtext.xbase.lib.Pair;
import com.google.common.base.Function;
import com.google.common.collect.Lists;
@ -38,7 +35,7 @@ import com.google.inject.internal.Join;
/**
* @author Moritz Eysholdt - Initial contribution and API
*/
public class SerializerPDAProviderTest extends AbstractXtextTests {
public class ContextTypePDAProviderTest extends AbstractXtextTests {
private static class ToStr implements Function<ISerState, String> {
private Function<AbstractElement, String> ts = new GrammarElementTitleSwitch().showAssignments()
.hideCardinality().showQualified();
@ -59,21 +56,6 @@ public class SerializerPDAProviderTest extends AbstractXtextTests {
+ " with org.eclipse.xtext.common.Terminals "
+ "generate sequenceParserPDAProviderTest \"http://www.eclipse.org/2010/tmf/xtext/SequenceParserPDAProvider\" ";
// public void drawGrammar(String path, Grammar grammar) {
// try {
// IContextProvider contexts = get(IContextProvider.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),
// path + "-" + new Context2NameFunction().apply(ctx) + "_"
// + (type == null ? "null" : type.getName()) + "-PDA.pdf", "-T pdf");
// } catch (IOException e) {
// e.printStackTrace();
// }
// }
private List<Triple<EClass, EObject, String>> getContexts(Grammar grammar) {
final Context2NameFunction ctx2name = get(Context2NameFunction.class);
final IContextProvider contextProvider = get(IContextProvider.class);
@ -101,11 +83,14 @@ public class SerializerPDAProviderTest extends AbstractXtextTests {
PdaListFormatter<ISerState, RuleCall> formatter = new PdaListFormatter<ISerState, RuleCall>();
formatter.setStateFormatter(new ToStr());
formatter.setStackitemFormatter(new GrammarElementTitleSwitch().showAssignments().hideCardinality());
formatter.sortFollowers();
for (Triple<EClass, EObject, String> ctx : getContexts(grammar)) {
// System.out.println();
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(ISerializerPDAProvider.class).getPDA(ctx.getSecond(),
ctx.getFirst());
Pda<? extends ISerState, RuleCall> pda = get(IContextTypePDAProvider.class).getContextTypePDA(
ctx.getSecond(), ctx.getFirst());
result.add(" " + formatter.format((Pda<ISerState, RuleCall>) pda).replace("\n", "\n "));
}
return Join.join("\n", result);
@ -143,6 +128,35 @@ public class SerializerPDAProviderTest extends AbstractXtextTests {
assertEquals(expected.toString(), actual);
}
public void testLoop1() throws Exception {
String actual = getParserRule("Rule: ('x' x=ID*)*;");
StringBuilder expected = new StringBuilder();
expected.append("Rule_Rule:\n");
expected.append(" start -> 'x'\n");
expected.append(" 'x' -> 'x', stop, x=ID\n");
expected.append(" x=ID -> 'x', stop, x=ID\n");
expected.append("null_Rule:\n");
expected.append(" start -> 'x', stop\n");
expected.append(" 'x' -> 'x', stop");
assertEquals(expected.toString(), actual);
}
public void testLoop2() throws Exception {
String actual = getParserRule("Model: (('x' x+=ID*) | ('y' y+=ID*))*;");
StringBuilder expected = new StringBuilder();
expected.append("Model_Model:\n");
expected.append(" start -> 'x', 'y'\n");
expected.append(" 'x' -> 'x', 'y', stop, x+=ID\n");
expected.append(" 'y' -> 'x', 'y', stop, y+=ID\n");
expected.append(" x+=ID -> 'x', 'y', stop, x+=ID\n");
expected.append(" y+=ID -> 'x', 'y', stop, y+=ID\n");
expected.append("null_Model:\n");
expected.append(" start -> 'x', 'y', stop\n");
expected.append(" 'x' -> 'x', 'y', stop\n");
expected.append(" 'y' -> 'x', 'y', stop");
assertEquals(expected.toString(), actual);
}
public void testDelegation2() throws Exception {
String actual = getParserRule("Rule: Foo | Delegate1; Delegate1: 'del' Delegate2 bar=ID; Delegate2: val=ID; Foo: val2=ID;");
StringBuilder expected = new StringBuilder();
@ -224,7 +238,76 @@ public class SerializerPDAProviderTest extends AbstractXtextTests {
expected.append(" {Val} -> val=ID");
assertEquals(expected.toString(), actual);
}
public void testExpression2() throws Exception {
String actual = getParserRule("Addition returns Expr: Prim ({Add.left=current} '+' right=Prim)*; Prim returns Expr: {Val} name=ID | '(' Addition ')';");
StringBuilder expected = new StringBuilder();
expected.append("Add_Addition:\n");
expected.append(" start -> >>Prim, {Add.left=}\n");
expected.append(" '(' -> >>Addition\n");
expected.append(" ')' -> <<Prim\n");
expected.append(" '+' -> right=Prim\n");
expected.append(" <<Addition -> ')'\n");
expected.append(" <<Prim -> <<Addition, stop\n");
expected.append(" >>Addition -> >>Prim, {Add.left=}\n");
expected.append(" >>Prim -> '('\n");
expected.append(" right=Prim -> <<Addition, stop\n");
expected.append(" {Add.left=} -> '+'\n");
expected.append("Add_Addition_Add_1_0:\n");
expected.append(" start -> >>Prim, {Add.left=}\n");
expected.append(" '(' -> >>Addition\n");
expected.append(" ')' -> <<Prim\n");
expected.append(" '+' -> right=Prim\n");
expected.append(" <<Addition -> ')'\n");
expected.append(" <<Prim -> <<Addition, stop\n");
expected.append(" >>Addition -> >>Prim, {Add.left=}\n");
expected.append(" >>Prim -> '('\n");
expected.append(" right=Prim -> <<Addition, stop\n");
expected.append(" {Add.left=} -> '+'\n");
expected.append("Add_Prim:\n");
expected.append(" start -> '('\n");
expected.append(" '(' -> >>Addition\n");
expected.append(" ')' -> <<Prim, stop\n");
expected.append(" '+' -> right=Prim\n");
expected.append(" <<Addition -> ')'\n");
expected.append(" <<Prim -> <<Addition\n");
expected.append(" >>Addition -> >>Prim, {Add.left=}\n");
expected.append(" >>Prim -> '('\n");
expected.append(" right=Prim -> <<Addition\n");
expected.append(" {Add.left=} -> '+'\n");
expected.append("Val_Addition:\n");
expected.append(" start -> >>Prim\n");
expected.append(" '(' -> >>Addition\n");
expected.append(" ')' -> <<Prim\n");
expected.append(" <<Addition -> ')'\n");
expected.append(" <<Prim -> <<Addition, stop\n");
expected.append(" >>Addition -> >>Prim\n");
expected.append(" >>Prim -> '(', {Val}\n");
expected.append(" name=ID -> <<Prim\n");
expected.append(" {Val} -> name=ID\n");
expected.append("Val_Addition_Add_1_0:\n");
expected.append(" start -> >>Prim\n");
expected.append(" '(' -> >>Addition\n");
expected.append(" ')' -> <<Prim\n");
expected.append(" <<Addition -> ')'\n");
expected.append(" <<Prim -> <<Addition, stop\n");
expected.append(" >>Addition -> >>Prim\n");
expected.append(" >>Prim -> '(', {Val}\n");
expected.append(" name=ID -> <<Prim\n");
expected.append(" {Val} -> name=ID\n");
expected.append("Val_Prim:\n");
expected.append(" start -> '(', {Val}\n");
expected.append(" '(' -> >>Addition\n");
expected.append(" ')' -> <<Prim, stop\n");
expected.append(" <<Addition -> ')'\n");
expected.append(" <<Prim -> <<Addition\n");
expected.append(" >>Addition -> >>Prim\n");
expected.append(" >>Prim -> '(', {Val}\n");
expected.append(" name=ID -> <<Prim, stop\n");
expected.append(" {Val} -> name=ID");
assertEquals(expected.toString(), actual);
}
public void testOptionalDelegate() throws Exception {
String actual = getParserRule("Rule: Mand | Opt; Mand: 'm' mand=ID; Opt: 'o' opt=ID?;");
StringBuilder expected = new StringBuilder();
@ -240,11 +323,11 @@ public class SerializerPDAProviderTest extends AbstractXtextTests {
expected.append(" mand=ID -> <<Mand\n");
expected.append("Opt_Opt:\n");
expected.append(" start -> 'o'\n");
expected.append(" 'o' -> opt=ID, stop\n");
expected.append(" 'o' -> opt=ID\n");
expected.append(" opt=ID -> stop\n");
expected.append("Opt_Rule:\n");
expected.append(" start -> >>Opt\n");
expected.append(" 'o' -> opt=ID, <<Opt\n");
expected.append(" 'o' -> opt=ID\n");
expected.append(" <<Opt -> stop\n");
expected.append(" >>Opt -> 'o'\n");
expected.append(" opt=ID -> <<Opt\n");

View file

@ -104,7 +104,7 @@ public class SyntacticSequencerPDAProviderTest extends AbstractXtextTests {
protected String getParserRule(String body) throws Exception {
Grammar grammar = (Grammar) getModel(HEADER + body);
// drawGrammar("pdf/" + getName(), grammar);
// drawGrammar("pdf/" + getName(), grammar);
List<String> result = Lists.newArrayList();
for (Triple<EClass, EObject, String> ctx : getContexts(grammar)) {
String t = ctx.getFirst() == null ? "null" : ctx.getFirst().getName();
@ -540,7 +540,6 @@ public class SyntacticSequencerPDAProviderTest extends AbstractXtextTests {
String actual = getParserRule(grammar.toString());
StringBuilder expected = new StringBuilder();
expected.append("Optional_Optional:\n");
expected.append(" start stop\n");
expected.append(" start val1=ID\n");
expected.append(" start val2=ID\n");
expected.append(" start val3=ID\n");
@ -609,7 +608,7 @@ public class SyntacticSequencerPDAProviderTest extends AbstractXtextTests {
String actual = getParserRule(grammar.toString());
StringBuilder expected = new StringBuilder();
expected.append("Model_Model:\n");
expected.append(" start ('x' | 'y')* stop\n");
expected.append(" start ('x' | 'y')+ stop\n");
expected.append(" start ('x'* 'y')+ y+=ID\n");
expected.append(" start ('y'* 'x')+ x+=ID\n");
expected.append(" x+=ID ('x' | 'y')* stop\n");