mirror of
https://github.com/sigmasternchen/xtext-core
synced 2025-03-16 08:48:55 +00:00
[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:
parent
1919e978af
commit
e4f327c00b
15 changed files with 1142 additions and 373 deletions
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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();
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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");
|
|
@ -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");
|
||||
|
|
Loading…
Reference in a new issue