[serializer] re-implemented grammar constraint provider

It's now based on the NFAs from SemanticSequencerNFAProvider.
Therefore, it will now benefit from all NFA/PDA processing
that's happening up the chain.

Signed-off-by: Moritz Eysholdt <moritz.eysholdt@itemis.de>
This commit is contained in:
Moritz Eysholdt 2015-10-06 12:34:16 +02:00
parent 513f1462ca
commit ce58e99eaa
17 changed files with 911 additions and 3003 deletions

View file

@ -9,6 +9,7 @@ package org.eclipse.xtext.util.formallang;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@ -50,6 +51,12 @@ public class NfaToProduction {
protected abstract int getElementCount();
protected abstract void sort(Comparator<? super AbstractElementAlias<T>> comparator);
protected abstract T getFirstElement();
public abstract Collection<AbstractElementAlias<T>> getChildren();
public boolean isMany() {
return many;
}
@ -78,6 +85,23 @@ public class NfaToProduction {
}
protected class ElementAliasComparator<T> implements Comparator<AbstractElementAlias<T>> {
protected final Comparator<? super T> delegate;
public ElementAliasComparator(Comparator<? super T> delegate) {
super();
this.delegate = delegate;
}
@Override
public int compare(AbstractElementAlias<T> o1, AbstractElementAlias<T> o2) {
T e1 = o1.getFirstElement();
T e2 = o2.getFirstElement();
return delegate.compare(e1, e2);
}
}
protected static class AliasGrammarProvider<TOKEN> implements Production<AbstractElementAlias<TOKEN>, TOKEN> {
protected AbstractElementAlias<TOKEN> root;
@ -145,6 +169,7 @@ public class NfaToProduction {
children.add(child);
}
@Override
public Set<AbstractElementAlias<T>> getChildren() {
return children;
}
@ -157,6 +182,20 @@ public class NfaToProduction {
return result;
}
@Override
protected void sort(Comparator<? super AbstractElementAlias<T>> comparator) {
for (AbstractElementAlias<T> child : this.children)
child.sort(comparator);
List<AbstractElementAlias<T>> sorting = Lists.newArrayList(this.children);
Collections.sort(sorting, comparator);
this.children = Sets.newLinkedHashSet(sorting);
}
@Override
protected T getFirstElement() {
return this.children.iterator().next().getFirstElement();
}
}
protected static class ElementAlias<T> extends AbstractElementAlias<T> {
@ -181,6 +220,20 @@ public class NfaToProduction {
return 1;
}
@Override
protected void sort(Comparator<? super AbstractElementAlias<T>> comparator) {
}
@Override
protected T getFirstElement() {
return element;
}
@Override
public Collection<AbstractElementAlias<T>> getChildren() {
return Collections.emptyList();
}
}
protected static class GroupAlias<T> extends AbstractElementAlias<T> {
@ -201,6 +254,7 @@ public class NfaToProduction {
children.add(child);
}
@Override
public List<AbstractElementAlias<T>> getChildren() {
return children;
}
@ -212,6 +266,18 @@ public class NfaToProduction {
result += child.getElementCount();
return result;
}
@Override
protected void sort(Comparator<? super AbstractElementAlias<T>> comparator) {
for (AbstractElementAlias<T> child : this.children)
child.sort(comparator);
}
@Override
protected T getFirstElement() {
return this.children.get(0).getFirstElement();
}
}
protected static class StateAlias<TOKEN> {
@ -610,7 +676,8 @@ public class NfaToProduction {
return result;
}
protected <T> boolean isPreferredSplitState(Pair<Integer, StateAlias<T>> state1, Pair<Integer, StateAlias<T>> state2) {
protected <T> boolean isPreferredSplitState(Pair<Integer, StateAlias<T>> state1,
Pair<Integer, StateAlias<T>> state2) {
int count1 = state1.getSecond().getElement().getElementCount();
int count2 = state2.getSecond().getElement().getElementCount();
if (count1 != count2)
@ -624,6 +691,11 @@ public class NfaToProduction {
public <ELEMENT, STATE, TOKEN> ELEMENT nfaToGrammar(Nfa<STATE> nfa, Function<STATE, TOKEN> state2token,
ProductionFactory<ELEMENT, TOKEN> grammarFactory) {
return nfaToGrammar(nfa, state2token, null, grammarFactory);
}
public <ELEMENT, STATE, TOKEN> ELEMENT nfaToGrammar(Nfa<STATE> nfa, Function<STATE, TOKEN> state2token,
Comparator<? super TOKEN> order, ProductionFactory<ELEMENT, TOKEN> grammarFactory) {
StateAliasNfa<TOKEN> states = createNfa(nfa, state2token);
boolean changed = true;
// System.out.println("init: " + Joiner.on(" ").join(getAllStates(start)));
@ -660,14 +732,80 @@ public class NfaToProduction {
// e.printStackTrace();
// }
// }
AliasGrammarProvider<TOKEN> production = new AliasGrammarProvider<TOKEN>(states.getStart().getElement());
AbstractElementAlias<TOKEN> root = states.getStart().getElement();
if (excludeStartAndStop) {
root = removeStartAndStop(nfa, state2token, root);
}
normalize(root);
if (order != null)
root.sort(new ElementAliasComparator<TOKEN>(order));
AliasGrammarProvider<TOKEN> production = new AliasGrammarProvider<TOKEN>(root);
return new ProductionUtil().clone(production, grammarFactory);
}
protected <TOKEN, STATE> AbstractElementAlias<TOKEN> removeStartAndStop(Nfa<STATE> nfa,
Function<STATE, TOKEN> state2token, AbstractElementAlias<TOKEN> root) {
if (excludeStartAndStop && root instanceof GroupAlias<?>) {
GroupAlias<TOKEN> group = (GroupAlias<TOKEN>) root;
List<AbstractElementAlias<TOKEN>> children = group.getChildren();
if (children.size() > 1) {
AbstractElementAlias<TOKEN> first = children.get(0);
AbstractElementAlias<TOKEN> last = children.get(children.size() - 1);
if (first instanceof ElementAlias<?> && last instanceof ElementAlias<?>) {
TOKEN startToken = state2token.apply(nfa.getStart());
TOKEN stopToken = state2token.apply(nfa.getStop());
TOKEN firstToken = ((ElementAlias<TOKEN>) first).getElement();
TOKEN lastToken = ((ElementAlias<TOKEN>) last).getElement();
if (firstToken == startToken && lastToken == stopToken) {
if (children.size() == 3) {
return children.get(1);
} else {
children.remove(children.size() - 1);
children.remove(0);
return root;
}
}
}
}
}
return root;
}
protected <T> boolean collectMergeableOptions(boolean root, AbstractElementAlias<T> alt,
List<AbstractElementAlias<T>> result) {
boolean optional = alt.optional;
if ((root || !alt.isMany()) && alt instanceof AlternativeAlias<?>) {
for (AbstractElementAlias<T> child : ((AlternativeAlias<T>) alt).getChildren())
optional |= collectMergeableOptions(false, child, result);
} else {
result.add(alt);
alt.optional = false;
}
return optional;
}
protected <T> void normalize(AbstractElementAlias<T> element) {
if (element instanceof AlternativeAlias<?>) {
AlternativeAlias<T> alt = (AlternativeAlias<T>) element;
List<AbstractElementAlias<T>> mergeable = Lists.newArrayList();
alt.optional = collectMergeableOptions(true, element, mergeable);
alt.children = Sets.newLinkedHashSet(mergeable);
}
for (AbstractElementAlias<T> child : element.getChildren())
normalize(child);
}
public <ELEMENT, STATE> ELEMENT nfaToGrammar(Nfa<STATE> nfa, ProductionFactory<ELEMENT, STATE> grammarFactory) {
return nfaToGrammar(nfa, Functions.<STATE> identity(), grammarFactory);
}
private boolean excludeStartAndStop = false;
public NfaToProduction excludeStartAndStop() {
excludeStartAndStop = true;
return this;
}
protected <T> void splitState(StateAlias<T> state) {
if (state.getIncoming().size() >= state.getOutgoing().size()) {
for (StateAlias<T> in : Lists.newArrayList(state.getIncoming())) {

View file

@ -8,10 +8,10 @@
package org.eclipse.xtext.util.formallang;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
@ -19,14 +19,13 @@ import java.util.Stack;
import com.google.common.base.Function;
import com.google.common.base.Functions;
import com.google.common.base.Joiner;
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;
/**
@ -171,7 +170,7 @@ public class NfaUtil {
public <S, ITERABLE extends Iterable<? extends S>> boolean canReach(Nfa<S> nfa, Predicate<S> matcher) {
return find(nfa, Collections.singleton(nfa.getStart()), matcher) != null;
}
public <S, ITERABLE extends Iterable<? extends S>> boolean canReach(Nfa<S> nfa, S state, Predicate<S> matcher) {
return find(nfa, Collections.singleton(state), matcher) != null;
}
@ -321,13 +320,45 @@ public class NfaUtil {
});
}
public <S> boolean equalsIgnoreOrder(Nfa<S> nfa1, Nfa<S> nfa2, Function<S, ? super Object> keyFunc) {
/**
* returns the sum of all edge-hashes.
*
* An edge-hash is computed as precedingStateKey.hashCode * (followingStateKey.hashCode + 1). Adding 1 ensures the
* direction of edges is considered.
*
* Disadvantage of this implementation: it calls keyFunc and key.hashCode twice on each state.
*/
public <S> int hashCodeIgnoreOrder(Nfa<S> nfa, Function<S, ? extends Object> keyFunc) {
int result = 0;
LinkedList<S> remaining = new LinkedList<S>();
Set<S> visited = Sets.newHashSet();
remaining.add(nfa.getStart());
while (!remaining.isEmpty()) {
S state = remaining.removeFirst();
Object stateKey = keyFunc.apply(state);
int stateHash = stateKey == null ? 0 : stateKey.hashCode();
for (S follower : nfa.getFollowers(state)) {
Object followerKey = keyFunc.apply(follower);
int followerHash = followerKey == null ? 0 : followerKey.hashCode();
int edgeHash = stateHash * (followerHash + 1);
result += edgeHash;
if (visited.add(follower)) {
remaining.add(follower);
}
}
}
return result;
}
public <S> boolean equalsIgnoreOrder(Nfa<S> nfa1, Nfa<S> nfa2, Function<S, ? extends Object> keyFunc) {
if (nfa1 == nfa2)
return true;
if (!Objects.equal(keyFunc.apply(nfa1.getStart()), keyFunc.apply(nfa2.getStart())))
return false;
return equalsIgnoreOrder(nfa1, nfa2, nfa1.getStart(), nfa2.getStart(), keyFunc, Sets.<S> newHashSet());
}
public <S> boolean equalsIgnoreOrder(Nfa<S> nfa1, Nfa<S> nfa2, S s1, S s2, Function<S, ? super Object> keyFunc,
public <S> boolean equalsIgnoreOrder(Nfa<S> nfa1, Nfa<S> nfa2, S s1, S s2, Function<S, ? extends Object> keyFunc,
Set<S> visited) {
if (!visited.add(s1))
return true;
@ -335,18 +366,50 @@ public class NfaUtil {
Iterable<S> followers2 = nfa1.getFollowers(s2);
if (Iterables.size(followers1) != Iterables.size(followers2))
return false;
Multimap<? super Object, S> index = Multimaps.index(followers1, keyFunc);
Map<Object, S> index = Maps.newHashMap();
for (S f1 : followers1)
if (index.put(keyFunc.apply(f1), f1) != null)
return false;
for (S f : followers2) {
Object key2 = keyFunc.apply(f);
Collection<S> key1s = index.get(key2);
if (key1s.size() != 1)
return false;
if (!equalsIgnoreOrder(nfa1, nfa2, key1s.iterator().next(), f, keyFunc, visited))
S key1s = index.get(key2);
if (!equalsIgnoreOrder(nfa1, nfa2, key1s, f, keyFunc, visited))
return false;
}
return true;
}
public <S> String identityString(Nfa<S> nfa, Function<S, String> idFunc) {
Map<String, S> names = Maps.newHashMap();
Map<S, Integer> ids = Maps.newHashMap();
for (S s : collect(nfa)) {
String name = idFunc.apply(s);
if (name == null) {
name = "(null)";
}
if (s == nfa.getStart())
name = "start:" + name;
else if (s == nfa.getStop())
name = "stop:" + name;
names.put(name, s);
}
List<String> sorted = Lists.newArrayList(names.keySet());
Collections.sort(sorted);
for (int i = 0; i < sorted.size(); i++)
ids.put(names.get(sorted.get(i)), i);
List<String> result = Lists.newArrayListWithExpectedSize(sorted.size());
for (String name : sorted) {
S state = names.get(name);
Integer id = ids.get(state);
List<Integer> followers = Lists.newArrayList();
for (S f : nfa.getFollowers(state))
followers.add(ids.get(f));
Collections.sort(followers);
result.add(id + ":" + name + "->" + Joiner.on(",").join(followers));
}
return Joiner.on("\n").join(result);
}
public <S> Nfa<S> filter(final Nfa<S> nfa, final Predicate<S> filter) {
return new Nfa<S>() {
@ -517,5 +580,5 @@ public class NfaUtil {
public <S, COMP extends Comparable<COMP>> Nfa<S> sort(Nfa<S> nfa, Map<S, COMP> comparator) {
return sort(nfa, new MappedComparator<S, COMP>(comparator));
}
}

View file

@ -1,112 +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 org.eclipse.emf.common.util.TreeIterator;
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.CompoundElement;
import org.eclipse.xtext.GrammarUtil;
import org.eclipse.xtext.Keyword;
import org.eclipse.xtext.RuleCall;
import org.eclipse.xtext.TypeRef;
import org.eclipse.xtext.grammaranalysis.impl.AbstractCachingNFABuilder;
import org.eclipse.xtext.grammaranalysis.impl.AbstractNFAProvider;
import org.eclipse.xtext.grammaranalysis.impl.AbstractNFAState;
import org.eclipse.xtext.grammaranalysis.impl.AbstractNFATransition;
import org.eclipse.xtext.serializer.analysis.ActionFilterNFAProvider.ActionFilterState;
import org.eclipse.xtext.serializer.analysis.ActionFilterNFAProvider.ActionFilterTransition;
import com.google.inject.Singleton;
@Singleton
public class ActionFilterNFAProvider extends AbstractNFAProvider<ActionFilterState, ActionFilterTransition> {
public static class ActionFilterState extends AbstractNFAState<ActionFilterState, ActionFilterTransition> {
public ActionFilterState(AbstractElement element, NFABuilder<ActionFilterState, ActionFilterTransition> builder) {
super(element, builder);
}
}
public static class ActionFilterTransition extends AbstractNFATransition<ActionFilterState, ActionFilterTransition> {
public ActionFilterTransition(ActionFilterState source, ActionFilterState target, boolean ruleCall,
AbstractElement loopCenter) {
super(source, target, ruleCall, loopCenter);
}
}
public static class ActionFilterNFABuilder extends
AbstractCachingNFABuilder<ActionFilterState, ActionFilterTransition> {
@Override
public ActionFilterState createState(AbstractElement ele) {
return new ActionFilterState(ele, this);
}
@Override
protected ActionFilterTransition createTransition(ActionFilterState source, ActionFilterState target,
boolean isRuleCall, AbstractElement loopCenter) {
return new ActionFilterTransition(source, target, isRuleCall, loopCenter);
}
@Override
public boolean filter(AbstractElement ele) {
// never filter root elements
if (!(ele.eContainer() instanceof AbstractElement))
return false;
// filter unassigned keywords and token rule calls
if (!GrammarUtil.isAssigned(ele)) {
if (ele instanceof Keyword)
return true;
if (ele instanceof RuleCall) {
AbstractRule rule = ((RuleCall) ele).getRule();
TypeRef ruleType = rule.getType();
if (!(ruleType == null || ruleType.getClassifier() instanceof EClass))
return true;
}
}
// filter groups and alternatives, if they contain assigned actions
if (ele instanceof CompoundElement) {
TreeIterator<EObject> ti = ele.eAllContents();
while (ti.hasNext()) {
EObject obj = ti.next();
if (obj instanceof Action && ((Action) obj).getFeature() != null)
return true;
}
}
// don't filter, if there is a child or a sibling that is or contains an assigned action.
TreeIterator<EObject> ti = ele.eContainer().eAllContents();
while (ti.hasNext()) {
EObject obj = ti.next();
if (obj instanceof Action && ((Action) obj).getFeature() != null)
return false;
}
return true;
}
@Override
public NFADirection getDirection() {
return NFADirection.BACKWARD;
}
}
@Override
protected NFABuilder<ActionFilterState, ActionFilterTransition> createBuilder() {
return new ActionFilterNFABuilder();
}
}

View file

@ -0,0 +1,105 @@
/*******************************************************************************
* Copyright (c) 2015 itemis AG (http://www.itemis.eu) and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*******************************************************************************/
package org.eclipse.xtext.serializer.analysis;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.eclipse.emf.common.notify.Adapter;
import org.eclipse.emf.common.notify.impl.AdapterImpl;
import org.eclipse.emf.common.util.EList;
import org.eclipse.emf.common.util.TreeIterator;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.xtext.AbstractRule;
import org.eclipse.xtext.Grammar;
import org.eclipse.xtext.GrammarUtil;
import org.eclipse.xtext.util.EmfFormatter;
import com.google.common.base.Function;
import com.google.common.base.Joiner;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
/**
* @author Moritz Eysholdt - Initial contribution and API
*/
public class GrammarElementDeclarationOrder extends AdapterImpl implements Comparator<EObject> {
public static GrammarElementDeclarationOrder get(Grammar grammar) {
for (Adapter a : grammar.eAdapters())
if (a instanceof GrammarElementDeclarationOrder)
return (GrammarElementDeclarationOrder) a;
GrammarElementDeclarationOrder result = new GrammarElementDeclarationOrder(grammar);
grammar.eAdapters().add(result);
for (Grammar g : GrammarUtil.allUsedGrammars(grammar)) {
EList<Adapter> adapters = g.eAdapters();
Iterator<Adapter> it = adapters.iterator();
while (it.hasNext())
if (it.next() instanceof GrammarElementDeclarationOrder)
it.remove();
adapters.add(result);
}
return result;
}
protected Map<EObject, Integer> elementIDCache;
protected GrammarElementDeclarationOrder(Grammar grammar) {
elementIDCache = Maps.newHashMap();
List<Grammar> grammars = Lists.newArrayList(grammar);
grammars.addAll(GrammarUtil.allUsedGrammars(grammar));
int counter = 0;
for (Grammar g : grammars) {
elementIDCache.put(g, counter++);
for (AbstractRule rule : g.getRules()) {
elementIDCache.put(rule, counter++);
TreeIterator<EObject> iterator = rule.eAllContents();
while (iterator.hasNext()) {
elementIDCache.put(iterator.next(), counter++);
}
}
}
}
@Override
public int compare(EObject o1, EObject o2) {
Integer i1 = elementIDCache.get(o1);
Integer i2 = elementIDCache.get(o2);
return i1.compareTo(i2);
}
public int getElementID(EObject ele) {
Integer result = elementIDCache.get(ele);
if (result == null) {
Grammar grammar = GrammarUtil.getGrammar(ele);
if (!elementIDCache.containsKey(grammar)) {
String grammarName = grammar.getName() + "#" + System.identityHashCode(grammar);
List<String> indexed = Lists.newArrayList();
for (EObject o : elementIDCache.keySet())
if (o instanceof Grammar)
indexed.add(((Grammar) o).getName() + "#" + System.identityHashCode(o));
throw new IllegalStateException("No ID found. Wrong grammar. \nRequested: " + grammarName
+ "\nAvailable: " + Joiner.on(", ").join(indexed));
} else
throw new IllegalStateException("No ID found. Not indexed. \nElement: " + EmfFormatter.objPath(ele));
}
return result;
}
public <T> Comparator<T> toComparator(final Function<T, EObject> elementAccess) {
return new Comparator<T>() {
@Override
public int compare(T o1, T o2) {
return GrammarElementDeclarationOrder.this.compare(elementAccess.apply(o1), elementAccess.apply(o2));
}
};
}
}

View file

@ -1,5 +1,6 @@
package org.eclipse.xtext.serializer.analysis;
import java.util.Collection;
import java.util.List;
import org.eclipse.emf.ecore.EClass;
@ -7,11 +8,19 @@ import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EStructuralFeature;
import org.eclipse.xtext.AbstractElement;
import org.eclipse.xtext.Action;
import org.eclipse.xtext.CrossReference;
import org.eclipse.xtext.Alternatives;
import org.eclipse.xtext.EnumRule;
import org.eclipse.xtext.Grammar;
import org.eclipse.xtext.GrammarUtil;
import org.eclipse.xtext.Group;
import org.eclipse.xtext.Keyword;
import org.eclipse.xtext.ParserRule;
import org.eclipse.xtext.RuleCall;
import org.eclipse.xtext.util.Pair;
import org.eclipse.xtext.TerminalRule;
import org.eclipse.xtext.UnorderedGroup;
import org.eclipse.xtext.serializer.analysis.ISemanticSequencerNfaProvider.ISemState;
import org.eclipse.xtext.util.EmfFormatter;
import org.eclipse.xtext.util.formallang.Nfa;
import org.eclipse.xtext.util.formallang.Production;
import com.google.inject.ImplementedBy;
@ -49,7 +58,49 @@ public interface IGrammarConstraintProvider {
ASSIGNED_KEYWORD, //
ASSIGNED_PARSER_RULE_CALL, //
ASSIGNED_TERMINAL_RULE_CALL, //
GROUP,
GROUP, //
UNORDERED_GROUP;
public static ConstraintElementType getConstraintElementType(AbstractElement ele) {
if (ele instanceof Action) {
if (((Action) ele).getFeature() != null)
return ConstraintElementType.ASSIGNED_ACTION_CALL;
} else if (ele instanceof Alternatives) {
return ConstraintElementType.ALTERNATIVE;
} else if (ele instanceof Group) {
return ConstraintElementType.GROUP;
} else if (ele instanceof UnorderedGroup) {
return ConstraintElementType.UNORDERED_GROUP;
} else if (GrammarUtil.containingCrossReference(ele) != null) {
if (ele instanceof RuleCall) {
RuleCall rc = (RuleCall) ele;
if (rc.getRule() instanceof ParserRule)
return ConstraintElementType.ASSIGNED_CROSSREF_DATATYPE_RULE_CALL;
if (rc.getRule() instanceof TerminalRule)
return ConstraintElementType.ASSIGNED_CROSSREF_TERMINAL_RULE_CALL;
if (rc.getRule() instanceof EnumRule)
return ConstraintElementType.ASSIGNED_CROSSREF_ENUM_RULE_CALL;
} else if (ele instanceof Keyword)
return ConstraintElementType.ASSIGNED_CROSSREF_KEYWORD;
} else if (GrammarUtil.containingAssignment(ele) != null) {
if (ele instanceof RuleCall) {
RuleCall rc = (RuleCall) ele;
if (rc.getRule() instanceof ParserRule) {
if (rc.getRule().getType().getClassifier() instanceof EClass)
return ConstraintElementType.ASSIGNED_PARSER_RULE_CALL;
return ConstraintElementType.ASSIGNED_DATATYPE_RULE_CALL;
}
if (rc.getRule() instanceof TerminalRule)
return ConstraintElementType.ASSIGNED_TERMINAL_RULE_CALL;
if (rc.getRule() instanceof EnumRule)
return ConstraintElementType.ASSIGNED_ENUM_RULE_CALL;
} else if (ele instanceof Keyword) {
return ConstraintElementType.ASSIGNED_KEYWORD;
}
}
throw new RuntimeException("Unknown Grammar Element: " + EmfFormatter.objPath(ele));
}
}
/**
@ -61,26 +112,11 @@ public interface IGrammarConstraintProvider {
*/
public interface IConstraint extends Comparable<IConstraint> {
/**
* @return a list of all assignments represented by this constraint.
* {@link IConstraintElement#getAssignmentID()} returns an Assignment's index in this list. The order of
* the list reflects the order of the assignments in the constraint. Assignments are
* {@link IConstraintElement}s with {@link IConstraintElement#getType()} == ASSIGNED_*
*/
IConstraintElement[] getAssignments();
/**
* @return the root of the tree of {@link IConstraintElement} that defines this constraint.
*/
IConstraintElement getBody();
/**
* @return a list of all elements represented by this constraint. This is a flattened version of the tree
* returned by {@link #getBody()}. {@link IConstraintElement#getElementID()} returns an Assignment's
* index in this list. The order of the list reflects the order of the elements in the constraint.
*/
IConstraintElement[] getElements();
/**
* @return a list of {@link IFeatureInfo} for all {@link EStructuralFeature}s from the {@link EClass} returned
* by {@link #getType()} that have an assignment if this constraint. If there is no assignment for an
@ -88,10 +124,6 @@ public interface IGrammarConstraintProvider {
*/
IFeatureInfo[] getFeatures();
Iterable<IFeatureInfo> getSingleAssignementFeatures();
Iterable<IFeatureInfo> getMultiAssignementFeatures();
/**
* @return a name that is unique for a grammar and that aims to be human-readable.
*/
@ -103,6 +135,10 @@ public interface IGrammarConstraintProvider {
* @return This constraint only applies to EObjects of this type.
*/
EClass getType();
List<IConstraintContext> getContexts();
Nfa<ISemState> getNfa();
}
/**
@ -192,77 +228,33 @@ public interface IGrammarConstraintProvider {
*/
public interface IConstraintElement {
// valid for *_ACTION_CALL
Action getAction();
int getAssignmentID();
EObject getCallContext();
String getCardinality();
// valid for GROUP and ALTERNATIVE, null otherwise
List<IConstraintElement> getChildren();
Collection<IConstraintElement> getChildren();
IConstraintElement getContainer();
IConstraint getContainingConstraint();
// valid for *_CROSSREF_*
CrossReference getCrossReference();
// valid for *_CROSSREF_*
EClass getCrossReferenceType();
int getElementID();
// valid for ASSIGNED_*
EStructuralFeature getFeature();
int getFeatureAssignmentID();
// valid for ASSIGNED_*
IFeatureInfo getFeatureInfo();
// IFeatureInfo getFeatureInfo();
// returns a RuleCall, Keyword or Action. But never an Assignment or
// Cross Reference.
AbstractElement getGrammarElement();
// valid for *_KEYWORD
Keyword getKeyword();
// valid for *_RULE_CALL
RuleCall getRuleCall();
ConstraintElementType getType();
List<Pair<IConstraintElement, RelationalDependencyType>> getDependingAssignment();
List<IConstraintElement> getContainedAssignments();
boolean isCardinalityOneAmongAssignments(List<IConstraintElement> assignments);
boolean isMany();
/**
* @return true, if this element or one of its containers is isMany().
*/
boolean isManyRecursive(IConstraintElement root);
boolean isOptional();
boolean isRoot();
/**
* @return true, if this element or one of its containers is optional. Also true, if one of the containers is an
* alternative.
*/
boolean isOptionalRecursive(IConstraintElement root);
}
public interface IFeatureInfo {
IConstraintElement[] getAssignments();
List<IConstraintElement> getAssignments();
List<EObject> getCalledContexts();
@ -270,51 +262,11 @@ public interface IGrammarConstraintProvider {
EStructuralFeature getFeature();
/**
* @return true for ASSIGNED_*, if there are multiple IConstraintELements for the same EStructuralFeature, which
* refer to different keywords, rulecalls or cross references.
*/
boolean isContentValidationNeeded();
List<Pair<IFeatureInfo, RelationalDependencyType>> getDependingFeatures();
int getUpperBound();
int getLowerBound();
}
public enum RelationalDependencyType {
/**
* (b >= 1) => (a == 0)
*/
EXCLUDE_IF_SET,
/**
* (b == 0) => (a == 0)
*/
EXCLUDE_IF_UNSET,
/**
* (b >= 1) => (a >= 0)
*/
MANDATORY_IF_SET,
/**
* a == b
*/
SAME,
/**
* a >= b
*/
SAME_OR_MORE,
/**
* a <= b
*/
SAME_OR_LESS
}
final int MAX = Integer.MAX_VALUE;
/**

View file

@ -16,6 +16,7 @@ import org.eclipse.emf.ecore.EStructuralFeature;
import org.eclipse.xtext.AbstractElement;
import org.eclipse.xtext.util.formallang.Nfa;
import com.google.common.base.Function;
import com.google.inject.ImplementedBy;
/**
@ -24,6 +25,13 @@ import com.google.inject.ImplementedBy;
@ImplementedBy(SemanticSequencerNfaProvider.class)
public interface ISemanticSequencerNfaProvider {
public Function<ISemState, AbstractElement> GET_ASSIGNED_GRAMMAR_ELEMENT = new Function<ISemState, AbstractElement>() {
@Override
public AbstractElement apply(ISemState input) {
return input.getAssignedGrammarElement();
}
};
public interface ISemState {
BitSet getAllFollowerFeatures();
@ -38,7 +46,7 @@ public interface ISemanticSequencerNfaProvider {
int getOrderID();
List<AbstractElement> getToBeValidatedAssignedElements();
boolean isBooleanAssignment();
}

View file

@ -23,9 +23,8 @@ import org.eclipse.xtext.AbstractElement;
import org.eclipse.xtext.Action;
import org.eclipse.xtext.Assignment;
import org.eclipse.xtext.CrossReference;
import org.eclipse.xtext.EcoreUtil2;
import org.eclipse.xtext.Grammar;
import org.eclipse.xtext.GrammarUtil;
import org.eclipse.xtext.ParserRule;
import org.eclipse.xtext.grammaranalysis.impl.GrammarElementTitleSwitch;
import org.eclipse.xtext.serializer.analysis.ISyntacticSequencerPDAProvider.ISynAbsorberState;
import org.eclipse.xtext.serializer.analysis.ISyntacticSequencerPDAProvider.SynAbsorberNfaAdapter;
@ -34,8 +33,8 @@ import org.eclipse.xtext.util.Pair;
import org.eclipse.xtext.util.Tuples;
import org.eclipse.xtext.util.formallang.Nfa;
import org.eclipse.xtext.util.formallang.NfaFactory;
import org.eclipse.xtext.util.formallang.NfaGraphFormatter;
import org.eclipse.xtext.util.formallang.NfaUtil;
import org.eclipse.xtext.xtext.RuleNames;
import com.google.common.collect.HashMultimap;
import com.google.common.collect.Lists;
@ -55,6 +54,7 @@ public class SemanticSequencerNfaProvider implements ISemanticSequencerNfaProvid
protected final ISemState start;
protected final ISemState stop;
protected int hashCode = -1;
public SemNfa(ISemState starts, ISemState stops) {
super();
@ -62,6 +62,22 @@ public class SemanticSequencerNfaProvider implements ISemanticSequencerNfaProvid
this.stop = stops;
}
@Override
public int hashCode() {
if (hashCode == -1)
hashCode = new NfaUtil().hashCodeIgnoreOrder(this, GET_ASSIGNED_GRAMMAR_ELEMENT);
return hashCode;
}
@Override
public boolean equals(Object obj) {
if (obj == null || obj.getClass() != getClass())
return false;
if (obj == this)
return true;
return new NfaUtil().equalsIgnoreOrder(this, (SemNfa) obj, GET_ASSIGNED_GRAMMAR_ELEMENT);
}
@Override
public List<ISemState> getFollowers(ISemState node) {
return node.getFollowers();
@ -77,6 +93,11 @@ public class SemanticSequencerNfaProvider implements ISemanticSequencerNfaProvid
return stop;
}
@Override
public String toString() {
return new NfaGraphFormatter().format(this);
}
}
protected static class SemState implements ISemState {
@ -176,11 +197,6 @@ public class SemanticSequencerNfaProvider implements ISemanticSequencerNfaProvid
}
protected Map<AbstractElement, Integer> elementIDCache;
@Inject
protected RuleNames ruleNames;
@Inject
protected ISyntacticSequencerPDAProvider pdaProvider;
@ -196,17 +212,6 @@ public class SemanticSequencerNfaProvider implements ISemanticSequencerNfaProvid
return true;
}
protected int getElementID(AbstractElement ele) {
if (elementIDCache == null) {
elementIDCache = Maps.newHashMap();
int counter = 0;
for (ParserRule pr : ruleNames.getAllParserRules())
for (AbstractElement e : EcoreUtil2.getAllContentsOfType(pr, AbstractElement.class))
elementIDCache.put(e, counter++);
}
return elementIDCache.get(ele);
}
@Override
public Nfa<ISemState> getNFA(EObject context, EClass type) {
Pair<EObject, EClass> key = Tuples.create(context, type);
@ -222,7 +227,7 @@ public class SemanticSequencerNfaProvider implements ISemanticSequencerNfaProvid
if (type != null)
initContentValidationNeeded(type, nfa);
initRemainingFeatures(nfa.getStop(), util.inverse(nfa), Sets.<ISemState> newHashSet());
initOrderIDs(nfa);
initOrderIDs(GrammarUtil.getGrammar(context), nfa);
// System.out.println(new NfaFormatter().format(nfa));
resultCache.put(key, nfa);
return nfa;
@ -244,10 +249,11 @@ public class SemanticSequencerNfaProvider implements ISemanticSequencerNfaProvid
((SemState) state).contentValidationNeeded = Collections.emptyList();
}
protected void initOrderIDs(Nfa<ISemState> nfa) {
protected void initOrderIDs(Grammar grammar, Nfa<ISemState> nfa) {
GrammarElementDeclarationOrder order = GrammarElementDeclarationOrder.get(grammar);
for (ISemState state : new NfaUtil().collect(nfa))
if (state.getAssignedGrammarElement() != null)
((SemState) state).orderID = getElementID(state.getAssignedGrammarElement());
((SemState) state).orderID = order.getElementID((state.getAssignedGrammarElement()));
}
protected void initRemainingFeatures(ISemState state, Nfa<ISemState> inverseNfa, Set<ISemState> visited) {

View file

@ -1,81 +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 org.eclipse.xtext.AbstractElement;
import org.eclipse.xtext.Action;
import org.eclipse.xtext.Assignment;
import org.eclipse.xtext.GrammarUtil;
import org.eclipse.xtext.grammaranalysis.impl.AbstractCachingNFABuilder;
import org.eclipse.xtext.grammaranalysis.impl.AbstractNFAProvider;
import org.eclipse.xtext.grammaranalysis.impl.AbstractNFAState;
import org.eclipse.xtext.grammaranalysis.impl.AbstractNFATransition;
import org.eclipse.xtext.serializer.analysis.TypeFinderNFAProvider.TypeFinderState;
import org.eclipse.xtext.serializer.analysis.TypeFinderNFAProvider.TypeFinderTransition;
import com.google.inject.Singleton;
@Singleton
public class TypeFinderNFAProvider extends AbstractNFAProvider<TypeFinderState, TypeFinderTransition> {
public static class TypeFinderState extends AbstractNFAState<TypeFinderState, TypeFinderTransition> {
public TypeFinderState(AbstractElement element, NFABuilder<TypeFinderState, TypeFinderTransition> builder) {
super(element, builder);
}
}
public static class TypeFinderTransition extends AbstractNFATransition<TypeFinderState, TypeFinderTransition> {
public TypeFinderTransition(TypeFinderState source, TypeFinderState target, boolean ruleCall,
AbstractElement loopCenter) {
super(source, target, ruleCall, loopCenter);
}
}
public static class TypeFinderNFABuilder extends AbstractCachingNFABuilder<TypeFinderState, TypeFinderTransition> {
@Override
public TypeFinderState createState(AbstractElement ele) {
return new TypeFinderState(ele, this);
}
@Override
protected TypeFinderTransition createTransition(TypeFinderState source, TypeFinderState target,
boolean isRuleCall, AbstractElement loopCenter) {
return new TypeFinderTransition(source, target, isRuleCall, loopCenter);
}
@Override
public boolean filter(AbstractElement ele) {
// never filter root elements
if (!(ele.eContainer() instanceof AbstractElement))
return false;
if (ele instanceof Assignment || ele instanceof Action)
return false;
if (GrammarUtil.isUnassignedEObjectRuleCall(ele))
return false;
return true;
}
@Override
public NFADirection getDirection() {
return NFADirection.BACKWARD;
}
}
@Override
protected NFABuilder<TypeFinderState, TypeFinderTransition> createBuilder() {
return new TypeFinderNFABuilder();
}
}

View file

@ -62,10 +62,10 @@ public class ContextFinder implements IContextFinder {
@Inject
protected TransientValueUtil transientValueUtil;
@Inject
protected ITransientValueService transientValues;
@Inject
protected ISemanticNodeProvider nodesProvider;
@ -208,7 +208,8 @@ public class ContextFinder implements IContextFinder {
return result;
}
protected Map<IConstraint, List<EObject>> getConstraints(EObject semanticObject, Iterable<EObject> contextCandidates) {
protected Map<IConstraint, List<EObject>> getConstraints(EObject semanticObject,
Iterable<EObject> contextCandidates) {
Map<IConstraint, List<EObject>> result = Maps.newLinkedHashMap();
for (EObject ctx : contextCandidates) {
IConstraint constraint = constraints.get(Tuples.create(ctx, semanticObject.eClass()));
@ -248,26 +249,30 @@ public class ContextFinder implements IContextFinder {
return false;
}
protected boolean isMandatory(IFeatureInfo feature) {
if (feature == null)
return false;
for (IConstraintElement ce : feature.getAssignments())
if (!ce.isOptionalRecursive(null))
return true;
return false;
}
protected boolean isValidValueQuantity(IConstraint constraint, EObject semanicObj) {
if (constraint == null)
return false;
for (int featureID = 0; featureID < semanicObj.eClass().getFeatureCount(); featureID++) {
IFeatureInfo featureInfo = constraint.getFeatures()[featureID];
EStructuralFeature structuralFeature = semanicObj.eClass().getEStructuralFeature(featureID);
// TODO validated bounds of lists properly
ValueTransient trans = transientValueUtil.isTransient(semanicObj, structuralFeature);
if (trans == ValueTransient.NO && featureInfo == null)
return false;
if (trans == ValueTransient.YES && isMandatory(featureInfo))
return false;
switch (trans) {
case NO:
if (featureInfo == null)
return false;
if (featureInfo.getUpperBound() <= 0)
return false;
break;
case YES:
if (featureInfo == null)
break;
if (featureInfo.getLowerBound() > 0)
return false;
break;
case PREFERABLY:
break;
}
}
return true;
}

View file

@ -1,931 +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.sequencer;
import java.util.Arrays;
import java.util.BitSet;
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.Action;
import org.eclipse.xtext.GrammarUtil;
import org.eclipse.xtext.IGrammarAccess;
import org.eclipse.xtext.Keyword;
import org.eclipse.xtext.RuleCall;
import org.eclipse.xtext.nodemodel.ICompositeNode;
import org.eclipse.xtext.nodemodel.ILeafNode;
import org.eclipse.xtext.nodemodel.INode;
import org.eclipse.xtext.serializer.analysis.IGrammarConstraintProvider;
import org.eclipse.xtext.serializer.analysis.IGrammarConstraintProvider.IConstraint;
import org.eclipse.xtext.serializer.analysis.IGrammarConstraintProvider.IConstraintContext;
import org.eclipse.xtext.serializer.analysis.IGrammarConstraintProvider.IConstraintElement;
import org.eclipse.xtext.serializer.analysis.IGrammarConstraintProvider.IFeatureInfo;
import org.eclipse.xtext.serializer.analysis.IGrammarConstraintProvider.RelationalDependencyType;
import org.eclipse.xtext.serializer.sequencer.ISemanticNodeProvider.INodesForEObjectProvider;
import org.eclipse.xtext.serializer.sequencer.ITransientValueService.ValueTransient;
import org.eclipse.xtext.serializer.tokens.ICrossReferenceSerializer;
import org.eclipse.xtext.serializer.tokens.IEnumLiteralSerializer;
import org.eclipse.xtext.serializer.tokens.IKeywordSerializer;
import org.eclipse.xtext.serializer.tokens.IValueSerializer;
import org.eclipse.xtext.util.EmfFormatter;
import org.eclipse.xtext.util.Pair;
import org.eclipse.xtext.util.Tuples;
import com.google.common.base.Joiner;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import com.google.inject.Inject;
/**
* @author Moritz Eysholdt - Initial contribution and API
* @deprecated use {@link BacktrackingSemanticSequencer}
*/
@Deprecated
public class GenericSemanticSequencer extends AbstractSemanticSequencer {
protected abstract class Allocation {
public Allocation() {
super();
}
public abstract void accept(EObject semanticObj, IConstraintElement constraint);
@Override
public String toString() {
return toString("");
}
public abstract String toString(String prefix);
}
protected class AllocationValue extends Allocation {
protected int index;
protected INode node;
protected Object value;
protected boolean optional;
public AllocationValue(Object value, int index, boolean optional, INode node) {
super();
this.value = value;
this.index = index;
this.optional = optional;
this.node = node;
}
@Override
public void accept(EObject semanticObj, IConstraintElement constraint) {
acceptSemantic(semanticObj, constraint, value, index, node);
}
public INode getNode() {
return node;
}
public Object getValue() {
return value;
}
@Override
public String toString(String prefix) {
return value instanceof EObject ? EmfFormatter.objPath((EObject) value) : value.toString();
}
}
protected class AlternativeAllocation extends Allocation {
protected Quantity child;
public AlternativeAllocation(Quantity child) {
super();
this.child = child;
}
@Override
public void accept(EObject semanticObj, IConstraintElement constraint) {
child.accept(semanticObj);
}
protected Quantity getChild() {
return child;
}
@Override
public String toString(String prefix) {
String newPrefix = " " + prefix;
return "Alt-Choice {\n" + newPrefix + child.toString(newPrefix) + "\n" + prefix + "}";
}
}
protected abstract class Feature2Assignment {
public abstract IFeatureInfo getFeature();
public abstract List<AllocationValue> getValuesFor(IConstraintElement assignment);
public abstract boolean isAmbiguous();
@Override
public String toString() {
List<String> result = Lists.newArrayList();
for (IConstraintElement assign : getFeature().getAssignments()) {
result.add(assign + "=>(" + Joiner.on(", ").join(getValuesFor(assign)) + ")");
}
return Joiner.on(", ").join(result);
}
}
protected class GroupAllocation extends Allocation {
protected List<Quantity> children = Lists.newArrayList();
public GroupAllocation() {
super();
}
public GroupAllocation(List<Quantity> children) {
super();
this.children = children;
}
@Override
public void accept(EObject semanticObj, IConstraintElement constraint) {
for (Quantity q : children)
q.accept(semanticObj);
}
public void addChild(Quantity quantity) {
children.add(quantity);
}
public List<Quantity> getChildren() {
return children;
}
@Override
public String toString(String prefix) {
String newPrefix = " " + prefix;
StringBuilder r = new StringBuilder();
r.append("Group {");
for (Quantity child : children) {
r.append("\n");
r.append(newPrefix);
r.append(child.getConstraintElement());
r.append(" => ");
r.append(child.toString(newPrefix));
}
r.append("\n");
r.append(prefix);
r.append("}");
return r.toString();
}
}
protected class MVFeature2AssignmentAmbiguous extends Feature2Assignment {
protected List<IConstraintElement> assignments;
protected int[] quantities; //TODO: implement
protected List<AllocationValue> values;
public MVFeature2AssignmentAmbiguous(List<IConstraintElement> assignments, List<AllocationValue> values) {
super();
this.assignments = assignments;
this.values = values;
this.quantities = new int[assignments.get(0).getFeatureInfo().getAssignments().length];
}
@Override
public IFeatureInfo getFeature() {
return assignments.get(0).getFeatureInfo();
}
@Override
public List<AllocationValue> getValuesFor(IConstraintElement assignment) {
return assignments.contains(assignment) ? values : Collections.<AllocationValue> emptyList();
}
@Override
public boolean isAmbiguous() {
int undefs = 0;
for (IConstraintElement ass : assignments)
if (quantities[ass.getFeatureAssignmentID()] == UNDEFINED_QUANTITY)
undefs++;
return undefs > 1;
}
}
protected class MVFeature2AssignmentUnambiguous extends Feature2Assignment {
protected IConstraintElement assignment;
protected List<AllocationValue> values;
public MVFeature2AssignmentUnambiguous(IConstraintElement assignment, List<AllocationValue> values) {
super();
this.assignment = assignment;
this.values = values;
}
@Override
public IFeatureInfo getFeature() {
return assignment.getFeatureInfo();
}
@Override
public List<AllocationValue> getValuesFor(IConstraintElement assignment) {
return assignment == this.assignment ? values : Collections.<AllocationValue> emptyList();
}
@Override
public boolean isAmbiguous() {
return false;
}
}
protected static class Quantity {
protected IConstraintElement constraintElement;
protected List<? extends Allocation> instances;
public Quantity(IConstraintElement constraintElement, Allocation allocation) {
this.instances = Collections.singletonList(allocation);
this.constraintElement = constraintElement;
}
public Quantity(IConstraintElement constraintElement, List<? extends Allocation> allocation) {
this.instances = allocation;
this.constraintElement = constraintElement;
}
public void accept(EObject semanticObj) {
if (instances != null)
for (Allocation a : instances)
a.accept(semanticObj, constraintElement);
}
public List<? extends Allocation> getAllocations() {
return instances;
}
public IConstraintElement getConstraintElement() {
return constraintElement;
}
@Override
public String toString() {
return toString("");
}
public String toString(String prefix) {
if (instances == null)
return "(null)";
if (instances.isEmpty())
return "(empty)";
else if (!constraintElement.isMany() && instances.size() < 2) {
return instances.get(0).toString(prefix);
} else {
StringBuilder buf = new StringBuilder();
buf.append("[");
for (Allocation a : instances) {
buf.append("\n");
buf.append(prefix + " ");
buf.append(a.toString(prefix + " "));
}
buf.append("\n");
buf.append(prefix);
buf.append("]");
return buf.toString();
}
}
}
protected abstract class SVFeature2Assignment extends Feature2Assignment {
protected boolean optional;
protected AllocationValue value;
public SVFeature2Assignment(boolean optional, AllocationValue value) {
super();
this.optional = optional;
this.value = value;
}
}
protected class SVFeature2AssignmentAmbiguous extends SVFeature2Assignment {
protected List<IConstraintElement> assignments;
protected Boolean[] enabled;
public SVFeature2AssignmentAmbiguous(List<IConstraintElement> assignments, boolean optional,
AllocationValue value) {
super(optional, value);
this.assignments = assignments;
this.enabled = new Boolean[assignments.get(0).getFeatureInfo().getAssignments().length];
Arrays.fill(this.enabled, null);
}
@Override
public IFeatureInfo getFeature() {
return assignments.get(0).getFeatureInfo();
}
@Override
public List<AllocationValue> getValuesFor(IConstraintElement assignment) {
if (assignments.contains(assignment)) {
Boolean en = enabled[assignment.getFeatureAssignmentID()];
if (en == null && !isAmbiguous()) {
for (IConstraintElement ass : assignments)
if (enabled[ass.getFeatureAssignmentID()] == Boolean.TRUE)
return Collections.emptyList();
return Collections.singletonList(value);
}
if (Boolean.TRUE.equals(en))
return Collections.singletonList(value);
}
return Collections.emptyList();
}
@Override
public boolean isAmbiguous() {
int undefined = 0;
for (IConstraintElement ass : assignments)
if (enabled[ass.getFeatureAssignmentID()] == null)
undefined++;
return undefined > 1;
}
}
protected class SVFeature2AssignmentUnambiguous extends SVFeature2Assignment {
protected IConstraintElement assignment;
public SVFeature2AssignmentUnambiguous(IConstraintElement assignment, boolean optional, AllocationValue value) {
super(optional, value);
this.assignment = assignment;
}
@Override
public IFeatureInfo getFeature() {
return assignment.getFeatureInfo();
}
@Override
public List<AllocationValue> getValuesFor(IConstraintElement assignment) {
if (assignment == this.assignment)
return Collections.singletonList(value);
return Collections.emptyList();
}
@Override
public boolean isAmbiguous() {
return false;
}
}
public final static int MAX = Integer.MAX_VALUE;
protected final static int NO_ASSIGNMENT = -2;
public final static int UNDEF = -1;
protected final static int UNDEFINED_QUANTITY = -1;
protected List<IConstraintContext> constraintContexts;
protected Map<Pair<EObject, EClass>, IConstraint> constraints;
@Inject
protected ICrossReferenceSerializer crossRefSerializer;
@Inject
protected IEnumLiteralSerializer enumLiteralSerializer;
@Inject
protected IGrammarAccess grammarAccess;
@Inject
protected IGrammarConstraintProvider grammarConstraintProvider;
@Inject
protected IKeywordSerializer keywordSerializer;
@Inject
protected ITransientValueService transientValueService;
@Inject
protected IValueSerializer valueSerializer;
protected void acceptAction(Action action, EObject semanticChild, ICompositeNode node) {
if (sequenceAcceptor.enterAssignedAction(action, semanticChild, node)) {
masterSequencer.createSequence(action, semanticChild);
sequenceAcceptor.leaveAssignedAction(action, semanticChild);
}
}
protected void acceptEObjectRuleCall(RuleCall ruleCall, EObject semanticChild, ICompositeNode node) {
if (sequenceAcceptor.enterAssignedParserRuleCall(ruleCall, semanticChild, node)) {
masterSequencer.createSequence(ruleCall.getRule(), semanticChild);
sequenceAcceptor.leaveAssignedParserRuleCall(ruleCall, semanticChild);
}
}
protected boolean acceptSemantic(EObject semanticObj, IConstraintElement constr, Object value, int index, INode node) {
switch (constr.getType()) {
case ASSIGNED_ACTION_CALL:
acceptAction(constr.getAction(), (EObject) value, (ICompositeNode) node);
return true;
case ASSIGNED_PARSER_RULE_CALL:
acceptEObjectRuleCall(constr.getRuleCall(), (EObject) value, (ICompositeNode) node);
return true;
case ASSIGNED_CROSSREF_DATATYPE_RULE_CALL:
RuleCall datatypeRC = constr.getRuleCall();
EObject value1 = (EObject) value;
ICompositeNode node1 = (ICompositeNode) node;
String token1 = crossRefSerializer.serializeCrossRef(semanticObj,
GrammarUtil.containingCrossReference(datatypeRC), value1, node1, errorAcceptor);
sequenceAcceptor.acceptAssignedCrossRefDatatype(datatypeRC, token1, value1, index, node1);
return true;
case ASSIGNED_CROSSREF_TERMINAL_RULE_CALL:
RuleCall terminalRC = constr.getRuleCall();
EObject value2 = (EObject) value;
ILeafNode node2 = (ILeafNode) node;
String token2 = crossRefSerializer.serializeCrossRef(semanticObj,
GrammarUtil.containingCrossReference(terminalRC), value2, node2, errorAcceptor);
sequenceAcceptor.acceptAssignedCrossRefTerminal(terminalRC, token2, value2, index, node2);
return true;
case ASSIGNED_CROSSREF_ENUM_RULE_CALL:
RuleCall enumRC = constr.getRuleCall();
ICompositeNode node3 = (ICompositeNode) node;
EObject target3 = (EObject) value;
String token3 = crossRefSerializer.serializeCrossRef(semanticObj,
GrammarUtil.containingCrossReference(enumRC), target3, node3, errorAcceptor);
sequenceAcceptor.acceptAssignedCrossRefEnum(enumRC, token3, target3, index, node3);
return true;
case ASSIGNED_CROSSREF_KEYWORD:
Keyword kw0 = constr.getKeyword();
ILeafNode node0 = (ILeafNode) node;
EObject target0 = (EObject) value;
String token0 = crossRefSerializer.serializeCrossRef(semanticObj,
GrammarUtil.containingCrossReference(kw0), target0, node0, errorAcceptor);
sequenceAcceptor.acceptAssignedCrossRefKeyword(kw0, token0, target0, index, node0);
return true;
case ASSIGNED_DATATYPE_RULE_CALL:
RuleCall datatypeRC1 = constr.getRuleCall();
ICompositeNode node4 = (ICompositeNode) node;
String token4 = valueSerializer.serializeAssignedValue(semanticObj, datatypeRC1, value, node4,
errorAcceptor);
sequenceAcceptor.acceptAssignedDatatype(datatypeRC1, token4, value, index, node4);
return true;
case ASSIGNED_ENUM_RULE_CALL:
RuleCall enumRC1 = constr.getRuleCall();
ICompositeNode node5 = (ICompositeNode) node;
String token5 = enumLiteralSerializer.serializeAssignedEnumLiteral(semanticObj, enumRC1, value, node5,
errorAcceptor);
sequenceAcceptor.acceptAssignedEnum(enumRC1, token5, value, index, node5);
return true;
case ASSIGNED_TERMINAL_RULE_CALL:
RuleCall terminalRC1 = constr.getRuleCall();
ILeafNode node6 = (ILeafNode) node;
String token6 = valueSerializer.serializeAssignedValue(semanticObj, terminalRC1, value, node6,
errorAcceptor);
sequenceAcceptor.acceptAssignedTerminal(terminalRC1, token6, value, index, node6);
return true;
case ASSIGNED_KEYWORD:
Keyword keyword = constr.getKeyword();
ILeafNode node7 = (ILeafNode) node;
String token7 = keywordSerializer.serializeAssignedKeyword(semanticObj, keyword, value, node7,
errorAcceptor);
sequenceAcceptor.acceptAssignedKeyword(keyword, token7, value, index, node7);
return true;
case ALTERNATIVE:
case GROUP:
return false;
}
return false;
}
// protected void applydeterministicQuantities(IConstraint constraint, Feature2Assignment[] values) {
// boolean changed;
// do {
// changed = false;
// for (IConstraintElement assignment : constraint.getAssignments())
// if (values[assignment.getAssignmentID()] != null && values[assignment.getAssignmentID()].isAmbiguous()) {
// int min = getMin(values, assignment);
// int max = getMax(values, assignment);
// if (min == max && min != UNDEF) {
// values[assignment.getAssignmentID()].setQuantity(assignment, min);
// changed = true;
// // System.out.println("Setting quantity of " + assignment + " to " + min);
// }
// }
// } while (changed);
// }
protected boolean containsUnavailableFeature(Feature2Assignment[] values, IConstraintElement element,
IConstraintElement excludeAssignment) {
if (element.isOptional())
return false;
switch (element.getType()) {
case GROUP:
for (IConstraintElement a : element.getChildren())
if (containsUnavailableFeature(values, a, excludeAssignment))
return true;
return false;
case ALTERNATIVE:
for (IConstraintElement a : element.getChildren())
if (!containsUnavailableFeature(values, a, excludeAssignment))
return false;
return true;
case ASSIGNED_ACTION_CALL:
case ASSIGNED_CROSSREF_DATATYPE_RULE_CALL:
case ASSIGNED_CROSSREF_ENUM_RULE_CALL:
case ASSIGNED_CROSSREF_TERMINAL_RULE_CALL:
case ASSIGNED_CROSSREF_KEYWORD:
case ASSIGNED_DATATYPE_RULE_CALL:
case ASSIGNED_ENUM_RULE_CALL:
case ASSIGNED_KEYWORD:
case ASSIGNED_PARSER_RULE_CALL:
case ASSIGNED_TERMINAL_RULE_CALL:
Feature2Assignment f2a = values[element.getAssignmentID()];
if (f2a == null)
return true;
if (f2a.isAmbiguous())
return false;
if (f2a.getValuesFor(element).isEmpty())
return true;
return false;
}
return false;
}
@Override
public void createSequence(EObject context, EObject semanticObject) {
initConstraints();
IConstraint constraint = getConstraint(context, semanticObject.eClass());
// System.out.println("Constraint: " + constraint);
if (constraint == null) {
if (errorAcceptor != null)
errorAcceptor.accept(diagnosticProvider.createInvalidContextOrTypeDiagnostic(semanticObject, context));
return;
}
INodesForEObjectProvider nodes = nodeProvider.getNodesForSemanticObject(semanticObject, null);
Feature2Assignment[] values = createValues(semanticObject, constraint, nodes);
// System.out.println("Values: " + f2aToStr(constraint.getBody(), values));
// System.out.println("Values (Disambiguated): " + f2aToStr(constraint.getBody(), values));
if (constraint.getBody() != null) {
Quantity quant = new Quantity(constraint.getBody(), createUnambiguousAllocation(constraint.getBody(),
values));
// System.out.println("Quantity: " + quant + " EndQuantity");
// List<IGrammarValuePair> result = Lists.newArrayList();
quant.accept(semanticObject);
}
sequenceAcceptor.finish();
}
protected List<? extends Allocation> createUnambiguousAllocation(IConstraintElement constraint,
Feature2Assignment[] values) {
switch (constraint.getType()) {
case ALTERNATIVE:
List<Allocation> result = Lists.newArrayList();
for (IConstraintElement child : constraint.getChildren()) {
List<? extends Allocation> allocs = createUnambiguousAllocation(child, values);
if (allocs == null)
return null;
if (child.isMany()) {
Quantity q = new Quantity(child, allocs);
result.add(new AlternativeAllocation(q));
} else {
for (Allocation a : allocs) {
AlternativeAllocation alloc = new AlternativeAllocation(new Quantity(child, a));
result.add(alloc);
}
}
}
return result;
case GROUP:
int min = 0;
int max = Integer.MAX_VALUE;
List<Pair<IConstraintElement, List<? extends Allocation>>> children = Lists
.newArrayListWithExpectedSize(constraint.getChildren().size());
for (IConstraintElement child : constraint.getChildren()) {
List<? extends Allocation> allocs = createUnambiguousAllocation(child, values);
if (allocs == null)
return null;
if (allocs.size() > 0)
min = Math.max(min, child.isMany() ? 1 : allocs.size());
if (!child.isOptional())
max = Math.max(max, allocs.size());
children.add(Tuples.<IConstraintElement, List<? extends Allocation>> create(child, allocs));
}
if (max < min)
throw new RuntimeException("err"); // TODO: handle this error
List<Allocation> result2 = Lists.newArrayListWithExpectedSize(min);
for (int i = 0; i < min; i++) {
List<Quantity> ch = Lists.newArrayList();
for (Pair<IConstraintElement, List<? extends Allocation>> p : children) {
if (i < p.getSecond().size()) {
if (i == min - 1)
ch.add(new Quantity(p.getFirst(), p.getSecond().subList(i, p.getSecond().size())));
else
ch.add(new Quantity(p.getFirst(), p.getSecond().get(i)));
}
}
result2.add(new GroupAllocation(ch));
}
// System.out.println(constraint + " => " + result2);
return result2;
case ASSIGNED_ACTION_CALL:
case ASSIGNED_CROSSREF_DATATYPE_RULE_CALL:
case ASSIGNED_CROSSREF_ENUM_RULE_CALL:
case ASSIGNED_CROSSREF_TERMINAL_RULE_CALL:
case ASSIGNED_CROSSREF_KEYWORD:
case ASSIGNED_DATATYPE_RULE_CALL:
case ASSIGNED_ENUM_RULE_CALL:
case ASSIGNED_KEYWORD:
case ASSIGNED_PARSER_RULE_CALL:
case ASSIGNED_TERMINAL_RULE_CALL:
Feature2Assignment f2a = values[constraint.getAssignmentID()];
if (f2a == null)
return Collections.emptyList();
else if (!f2a.isAmbiguous()) {
return f2a.getValuesFor(constraint);
// List<? extends Allocation> r = f2a.getValuesFor(constraint);
// return r.isEmpty() ? null : r;
} else
return null;
}
return null;
}
protected Feature2Assignment[] createValues(EObject semanticObject, IConstraint constraint,
INodesForEObjectProvider nodes) {
Feature2Assignment[] result = new Feature2Assignment[constraint.getAssignments().length];
for (IFeatureInfo feature : constraint.getSingleAssignementFeatures()) {
if (feature.getFeature().isMany()) {
List<AllocationValue> allocs = getNonTransientValuesForMVFeature(semanticObject, feature, nodes);
if (!allocs.isEmpty()) {
IConstraintElement ass = feature.getAssignments()[0];
result[ass.getAssignmentID()] = new MVFeature2AssignmentUnambiguous(ass, allocs);
}
} else {
ValueTransient trans = transientValueService.isValueTransient(semanticObject, feature.getFeature());
if (trans != ValueTransient.YES) {
Object value = semanticObject.eGet(feature.getFeature());
INode node = nodes.getNodeForSingelValue(feature.getFeature(), value);
if (trans != ValueTransient.PREFERABLY || node != null) {
IConstraintElement ass = feature.getAssignments()[0];
AllocationValue alloc = new AllocationValue(value, -1, trans == ValueTransient.PREFERABLY, node);
result[ass.getAssignmentID()] = new SVFeature2AssignmentUnambiguous(ass,
trans == ValueTransient.PREFERABLY, alloc);
}
}
}
}
for (IFeatureInfo feature : constraint.getMultiAssignementFeatures()) {
if (feature.getFeature().isMany()) {
List<AllocationValue> allocs = getNonTransientValuesForMVFeature(semanticObject, feature, nodes);
if (!allocs.isEmpty())
createValues(semanticObject, feature, allocs, result);
} else {
ValueTransient trans = transientValueService.isValueTransient(semanticObject, feature.getFeature());
if (trans != ValueTransient.YES) {
Object value = semanticObject.eGet(feature.getFeature());
INode node = nodes.getNodeForSingelValue(feature.getFeature(), value);
AllocationValue alloc = new AllocationValue(value, -1, trans == ValueTransient.PREFERABLY, node);
createValues(semanticObject, feature, trans == ValueTransient.PREFERABLY, alloc, result);
}
}
}
return result;
}
protected void createValues(EObject semanticObj, IFeatureInfo feature, boolean optional, AllocationValue value,
Feature2Assignment[] target) {
List<IConstraintElement> remainingAssignments = Lists.newArrayList();
for (IConstraintElement ass : feature.getAssignments())
if (!isExcludedByDependees(ass, target))
remainingAssignments.add(ass);
if (!remainingAssignments.isEmpty() && feature.isContentValidationNeeded())
remainingAssignments = findValidAssignments(semanticObj,
remainingAssignments.toArray(new IConstraintElement[remainingAssignments.size()]), value);
if (remainingAssignments.isEmpty())
return; // TODO: handle this error, no valid assignment has been found for the value.
else if (remainingAssignments.size() == 1) {
IConstraintElement ass = remainingAssignments.get(0);
target[ass.getAssignmentID()] = new SVFeature2AssignmentUnambiguous(ass, optional, value);
return;
}
SVFeature2AssignmentAmbiguous f2a = new SVFeature2AssignmentAmbiguous(remainingAssignments, optional, value);
for (IConstraintElement ass : remainingAssignments)
target[ass.getAssignmentID()] = f2a;
}
protected void createValues(EObject semanticObj, IFeatureInfo feature, List<AllocationValue> values,
Feature2Assignment[] target) {
List<IConstraintElement> remainingAssignments = Lists.newArrayList();
for (IConstraintElement ass : feature.getAssignments())
if (!isExcludedByDependees(ass, target))
remainingAssignments.add(ass);
if (feature.isContentValidationNeeded())
remainingAssignments = findValidAssignments(semanticObj, remainingAssignments, values);
if (remainingAssignments.size() == 0)
throw new RuntimeException("no valid assignments"); // TODO: handle this better
if (remainingAssignments.size() == 1) {
IConstraintElement ass = remainingAssignments.get(0);
target[ass.getAssignmentID()] = new MVFeature2AssignmentUnambiguous(ass, values);
return;
}
List<AllocationValue> remainingValues = Lists.newArrayList(values);
distributeValuesByQuantity(remainingAssignments, remainingValues, target);
if (remainingAssignments.size() == 1) {
IConstraintElement ass = remainingAssignments.get(0);
target[ass.getAssignmentID()] = new MVFeature2AssignmentUnambiguous(ass, remainingValues);
return;
}
MVFeature2AssignmentAmbiguous f2a = new MVFeature2AssignmentAmbiguous(remainingAssignments, remainingValues);
for (IConstraintElement ass : remainingAssignments)
target[ass.getAssignmentID()] = f2a;
}
protected void distributeValuesByQuantity(List<IConstraintElement> assignments, List<AllocationValue> values,
Feature2Assignment[] target) {
while (true) {
IConstraintElement ass = assignments.get(0);
if (ass.isCardinalityOneAmongAssignments(assignments)) {
target[ass.getAssignmentID()] = new SVFeature2AssignmentUnambiguous(ass, false, values.get(0));
values.remove(0);
assignments.remove(0);
} else
break;
}
for (int i = assignments.size() - 1; i >= 0 && !values.isEmpty(); i--) {
IConstraintElement ass = assignments.get(i);
if (ass != null && ass.isCardinalityOneAmongAssignments(assignments)) {
target[ass.getAssignmentID()] = new SVFeature2AssignmentUnambiguous(ass, false, values.get(values
.size() - 1));
values.remove(values.size() - 1);
assignments.remove(i);
} else
break;
}
// for (int i = assignments.size() - 1; i >= 0; i--)
// if (assignments.get(i) == null)
// assignments.remove(i);
}
protected String f2aToStr(IConstraintElement ele, Feature2Assignment[] values) {
if (ele == null)
return "(null)";
StringBuilder result = new StringBuilder();
f2aToStr(ele, values, "", result);
return result.toString();
}
protected void f2aToStr(IConstraintElement ele, Feature2Assignment[] values, String prefix, StringBuilder result) {
result.append(prefix);
if (ele.getChildren() != null) {
result.append(ele.getType().name());
result.append(ele.getCardinality());
result.append(" {\n");
for (IConstraintElement child : ele.getChildren())
f2aToStr(child, values, prefix + " ", result);
result.append(prefix);
result.append("}\n");
} else if (ele.getAssignmentID() >= 0) {
result.append(ele.toString());
result.append(" => ");
Feature2Assignment value = values[ele.getAssignmentID()];
if (value != null) {
if (value.isAmbiguous())
result.append("ambiguous!");
result.append(Joiner.on(", ").join(values[ele.getAssignmentID()].getValuesFor(ele)));
}
result.append("\n");
}
}
@Inject
protected IAssignmentFinder assignmentFinder;
protected List<IConstraintElement> findValidAssignments(EObject semanticObj, IConstraintElement[] assignments,
AllocationValue value) {
List<AbstractElement> assignedElements = Lists.newArrayList();
for (IConstraintElement ass : assignments)
assignedElements.add(ass.getGrammarElement());
Set<AbstractElement> assignedElements2 = Sets.newHashSet(assignmentFinder.findAssignmentsByValue(semanticObj,
assignedElements, value.getValue(), value.getNode()));
List<IConstraintElement> result = Lists.newArrayList();
for (IConstraintElement ass : assignments)
if (assignedElements2.contains(ass.getGrammarElement()))
result.add(ass);
return result;
}
protected List<IConstraintElement> findValidAssignments(EObject semanticObj, List<IConstraintElement> assignments,
List<AllocationValue> values) {
BitSet bs = new BitSet();
IConstraintElement[] assignmentsAr = assignments.toArray(new IConstraintElement[assignments.size()]);
for (AllocationValue value : values)
for (IConstraintElement validAssignments : findValidAssignments(semanticObj, assignmentsAr, value))
bs.set(validAssignments.getFeatureAssignmentID());
List<IConstraintElement> result = Lists.newArrayList();
for (IConstraintElement ass : assignments)
if (bs.get(ass.getFeatureAssignmentID()))
result.add(ass);
return result;
}
protected IConstraint getConstraint(EObject context, EClass type) {
return constraints.get(Tuples.create(context, type));
}
protected List<AllocationValue> getNonTransientValuesForMVFeature(EObject semanticObject, IFeatureInfo feature,
INodesForEObjectProvider nodes) {
switch (transientValueService.isListTransient(semanticObject, feature.getFeature())) {
case NO:
List<AllocationValue> allocs1 = Lists.newArrayList();
List<?> values1 = (List<?>) semanticObject.eGet(feature.getFeature());
for (int i = 0; i < values1.size(); i++) {
Object value = values1.get(i);
INode node = nodes.getNodeForMultiValue(feature.getFeature(), i, i, value);
allocs1.add(new AllocationValue(value, i, false, node));
}
return allocs1;
case SOME:
List<AllocationValue> allocs2 = Lists.newArrayList();
List<?> values2 = (List<?>) semanticObject.eGet(feature.getFeature());
for (int i = 0, j = 0; i < values2.size(); i++)
if (!transientValueService.isValueInListTransient(semanticObject, i, feature.getFeature())) {
Object value = values2.get(i);
INode node = nodes.getNodeForMultiValue(feature.getFeature(), i, j++, value);
allocs2.add(new AllocationValue(value, i, false, node));
}
return allocs2;
case YES:
}
return Collections.emptyList();
}
protected void initConstraints() {
if (constraintContexts == null) {
constraints = Maps.newLinkedHashMap();
constraintContexts = grammarConstraintProvider.getConstraints(grammarAccess.getGrammar());
// System.out.println(Joiner.on("\n").join(constraintContexts));
for (IConstraintContext ctx : constraintContexts)
for (IConstraint constraint : ctx.getConstraints())
constraints.put(Tuples.create(ctx.getContext(), constraint.getType()), constraint);
}
}
protected boolean isAmbiguous(Feature2Assignment[] allocations) {
for (Feature2Assignment feat : allocations)
if (feat.isAmbiguous())
return true;
return false;
}
protected boolean isExcludedByDependees(IConstraintElement assignments, Feature2Assignment[] target) {
List<Pair<IConstraintElement, RelationalDependencyType>> dependees = assignments.getDependingAssignment();
if (dependees == null || dependees.isEmpty())
return false;
for (Pair<IConstraintElement, RelationalDependencyType> e : dependees)
switch (e.getSecond()) {
case EXCLUDE_IF_SET:
if (target[e.getFirst().getAssignmentID()] != null)
return true;
break;
case SAME:
case SAME_OR_LESS:
case EXCLUDE_IF_UNSET:
if (target[e.getFirst().getAssignmentID()] == null
&& e.getFirst().getFeatureInfo().getAssignments().length == 1)
return true;
break;
case MANDATORY_IF_SET:
case SAME_OR_MORE:
}
return false;
}
}

View file

@ -1,190 +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;
import org.eclipse.xtext.serializer.sequencer.GenericSemanticSequencer;
import org.eclipse.xtext.serializer.sequencer.ISemanticSequencer;
import org.junit.Test;
/**
* @author Moritz Eysholdt - Initial contribution and API
*/
@SuppressWarnings("deprecation")
public class GenericSemanticSequencerTest extends AbstractSemanticSequencerTest {
@Override
protected ISemanticSequencer getGenericSemanticSequencer() {
return get(GenericSemanticSequencer.class);
}
@Override
@Test public void testUnorderedAlternative1() throws Exception {
// unsupported
}
@Override
@Test public void testUnorderedAlternative2() throws Exception {
// unsupported
}
@Override
@Test public void testUnorderedAlternative3() throws Exception {
// unsupported
}
@Override
@Test public void testUnorderedAlternative4() throws Exception {
// unsupported
}
@Override
@Test public void testUnorderedAlternative5() throws Exception {
// unsupported
}
@Override
@Test public void testUnorderedAlternative6() throws Exception {
// unsupported
}
@Override
@Test public void testUnorderedAlternative7() throws Exception {
// unsupported
}
@Override
@Test public void testUnorderedGroup1() throws Exception {
// unsupported
}
@Override
@Test public void testUnorderedGroup2() throws Exception {
// unsupported
}
@Override
@Test public void testUnorderedGroup3() throws Exception {
// unsupported
}
@Override
@Test public void testUnorderedGroup4() throws Exception {
// unsupported
}
@Override
@Test public void testUnorderedGroup5() throws Exception {
// unsupported
}
@Override
@Test public void testUnorderedGroup6() throws Exception {
// unsupported
}
@Override
@Test public void testUnorderedGroup7() throws Exception {
// unsupported
}
@Override
@Test public void testUnorderedGroup8() throws Exception {
// unsupported
}
@Override
@Test public void testUnorderedGroupOptional1() throws Exception {
// unsupported
}
@Override
@Test public void testUnorderedGroupOptional2() throws Exception {
// unsupported
}
@Override
@Test public void testUnorderedGroupOptional3() throws Exception {
// unsupported
}
@Override
@Test public void testUnorderedGroupOptional4() throws Exception {
// unsupported
}
@Override
@Test public void testUnorderedGroupOptional5() throws Exception {
// unsupported
}
@Override
@Test public void testUnorderedGroupOptional6() throws Exception {
// unsupported
}
@Override
@Test public void testUnorderedGroupOptional7() throws Exception {
// unsupported
}
@Override
@Test public void testUnorderedGroupBoolean1() throws Exception {
// unsupported
}
@Override
@Test public void testUnorderedGroupBoolean2() throws Exception {
// unsupported
}
@Override
@Test public void testUnorderedGroupBoolean3() throws Exception {
// unsupported
}
@Override
@Test public void testUnorderedGroupBoolean4() throws Exception {
// unsupported
}
@Override
@Test public void testUnorderedGroupBoolean5() throws Exception {
// unsupported
}
@Override
@Test public void testUnorderedGroupBoolean6() throws Exception {
// unsupported
}
@Override
@Test public void testUnorderedGroupBoolean7() throws Exception {
// unsupported
}
@Override
@Test public void testUnorderedGroupBoolean8() throws Exception {
// unsupported
}
@Override
@Test public void testSingleKeyword1OrID() throws Exception {
// unsupported
}
@Override
@Test public void testSingleKeywordOrID2() throws Exception {
// unsupported
}
@Override
@Test public void testSingleKeywordOrID3() throws Exception {
// unsupported
}
}

View file

@ -275,9 +275,9 @@ public class GrammarConstraintProviderAssignedActionTest extends AbstractXtextTe
StringBuilder expected = new StringBuilder();
expected.append("Assignment: Addition_Assignment_Multiplication_Bin | Prim_Val;\n");
expected.append(" Addition_Assignment_Multiplication_Bin returns Bin: (\n");
expected.append(" (left+=Assignment_Bin_1_0 op='=' right=Addition) | \n");
expected.append(" (left+=Addition_Bin_1_0 op='+' right=Multiplication) | \n");
expected.append(" (left+=Multiplication_Bin_1_0 op='*' right=Prim) | \n");
expected.append(" (left+=Assignment_Bin_1_0 op='=' right=Addition)\n");
expected.append(" (left+=Multiplication_Bin_1_0 op='*' right=Prim)\n");
expected.append(");\n");
expected.append(" Prim_Val returns Val: name=ID;\n");
expected.append("Assignment_Bin_1_0: Addition_Assignment_Multiplication_Bin | Prim_Val;\n");
@ -326,17 +326,17 @@ public class GrammarConstraintProviderAssignedActionTest extends AbstractXtextTe
@Test
public void testActionSequence3() throws Exception {
StringBuilder grammar = new StringBuilder();
grammar.append("Rule: val1=ID ({A.a1=current} a2=ID ({A.a1=current} a2=ID ({A.a1=current} a2=ID)?)?)?;\n");
grammar.append("Rule: v0=ID ({A.a1=current} v1=ID ({A.a1=current} v2=ID ({A.a1=current} v3=ID)?)?)?;\n");
String actual = getParserRule(grammar.toString());
StringBuilder expected = new StringBuilder();
expected.append("Rule: Rule_A | Rule_Rule;\n");
expected.append(" Rule_A returns A: ((a1=Rule_A_1_2_2_0 a2=ID) | (a1=Rule_A_1_2_0 a2=ID) | (a1=Rule_A_1_0 a2=ID));\n");
expected.append(" Rule_Rule returns Rule: val1=ID;\n");
expected.append(" Rule_A returns A: ((a1=Rule_A_1_0 v1=ID) | (a1=Rule_A_1_2_0 v2=ID) | (a1=Rule_A_1_2_2_0 v3=ID));\n");
expected.append(" Rule_Rule returns Rule: v0=ID;\n");
expected.append("Rule_A_1_0: Rule_Rule;\n");
expected.append("Rule_A_1_2_0: Rule_A_1_2_0_A;\n");
expected.append(" Rule_A_1_2_0_A returns A: (a1=Rule_A_1_0 a2=ID);\n");
expected.append(" Rule_A_1_2_0_A returns A: (a1=Rule_A_1_0 v1=ID);\n");
expected.append("Rule_A_1_2_2_0: Rule_A_1_2_2_0_A;\n");
expected.append(" Rule_A_1_2_2_0_A returns A: (a1=Rule_A_1_2_0 a2=ID);");
expected.append(" Rule_A_1_2_2_0_A returns A: (a1=Rule_A_1_2_0 v2=ID);");
assertEquals(expected.toString(), actual);
}
@ -455,7 +455,7 @@ public class GrammarConstraintProviderAssignedActionTest extends AbstractXtextTe
String actual = getParserRule(grammar.toString());
StringBuilder expected = new StringBuilder();
expected.append("Expr: Abs_Expr_Op | Prim_NumberLiteral;\n");
expected.append(" Abs_Expr_Op returns Op: ((op='ABS' rhs=Prim) | ((op='+' | op='-') rhs=Abs));\n");
expected.append(" Abs_Expr_Op returns Op: (((op='+' | op='-') rhs=Abs) | (op='ABS' rhs=Prim));\n");
expected.append(" Prim_NumberLiteral returns NumberLiteral: value=INT;\n");
expected.append("Abs: Abs_Expr_Op | Prim_NumberLiteral;\n");
expected.append("Prim: Abs_Expr_Op | Prim_NumberLiteral;");
@ -471,10 +471,10 @@ public class GrammarConstraintProviderAssignedActionTest extends AbstractXtextTe
String actual = getParserRule(grammar.toString());
StringBuilder expected = new StringBuilder();
expected.append("Ex1: Ex1_Ex2_Ex3_o | Ex3_Ex;\n");
expected.append(" Ex1_Ex2_Ex3_o returns o: ((l=Ex2_o_1_0 o='c' r=Ex3) | (l=Ex3_o_1_0 o='d' name=ID) | (l=Ex1_o_1_0_0 o='a' r=Ex2) | (l=Ex1_o_1_1_0 o='b' r=Ex2));\n");
expected.append(" Ex1_Ex2_Ex3_o returns o: ((l=Ex1_o_1_0_0 o='a' r=Ex2) | (l=Ex1_o_1_1_0 o='b' r=Ex2) | (l=Ex2_o_1_0 o='c' r=Ex3) | (l=Ex3_o_1_0 o='d' name=ID));\n");
expected.append(" Ex3_Ex returns Ex: name=ID;\n");
expected.append("Ex1_o_1_0_0: Ex1_Ex2_Ex3_o_1_0_0_o | Ex3_Ex;\n");
expected.append(" Ex1_Ex2_Ex3_o_1_0_0_o returns o: ((l=Ex2_o_1_0 o='c' r=Ex3) | (l=Ex3_o_1_0 o='d' name=ID) | (l=Ex1_o_1_0_0 o='a' r=Ex2));\n");
expected.append(" Ex1_Ex2_Ex3_o_1_0_0_o returns o: ((l=Ex1_o_1_0_0 o='a' r=Ex2) | (l=Ex2_o_1_0 o='c' r=Ex3) | (l=Ex3_o_1_0 o='d' name=ID));\n");
expected.append("Ex1_o_1_1_0: Ex2_Ex3_o | Ex3_Ex;\n");
expected.append(" Ex2_Ex3_o returns o: ((l=Ex2_o_1_0 o='c' r=Ex3) | (l=Ex3_o_1_0 o='d' name=ID));\n");
expected.append("Ex2: Ex2_Ex3_o | Ex3_Ex;\n");

View file

@ -1,253 +0,0 @@
/*******************************************************************************
* Copyright (c) 2010 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.List;
import org.eclipse.xtext.Grammar;
import org.eclipse.xtext.XtextStandaloneSetup;
import org.eclipse.xtext.junit4.AbstractXtextTests;
import org.eclipse.xtext.serializer.analysis.IGrammarConstraintProvider;
import org.eclipse.xtext.serializer.analysis.IGrammarConstraintProvider.IConstraint;
import org.eclipse.xtext.serializer.analysis.IGrammarConstraintProvider.IConstraintElement;
import org.eclipse.xtext.serializer.analysis.IGrammarConstraintProvider.RelationalDependencyType;
import org.eclipse.xtext.util.Pair;
import org.junit.Test;
import com.google.common.base.Joiner;
import com.google.common.collect.Lists;
/**
* @author Moritz Eysholdt - Initial contribution and API
*/
public class GrammarConstraintProviderAssignmentsTest extends AbstractXtextTests {
@Override
public void setUp() throws Exception {
super.setUp();
with(XtextStandaloneSetup.class);
}
final static String HEADER = "grammar org.eclipse.xtext.validation.GrammarConstraintTestLanguage"
+ " with org.eclipse.xtext.common.Terminals "
+ "generate grammarConstraintTest \"http://www.eclipse.org/2010/tmf/xtext/GCT\" ";
private String getParserRule(String body) throws Exception {
Grammar grammar = (Grammar) getModel(HEADER + body);
IGrammarConstraintProvider gcp = get(IGrammarConstraintProvider.class);
IConstraint ctxts = gcp.getConstraints(grammar).get(0).getConstraints().get(0);
List<String> result = Lists.newArrayList();
for (IConstraintElement ass : ctxts.getBody().getContainedAssignments()) {
result.add(ass.toString());
for (Pair<IConstraintElement, RelationalDependencyType> c : ass.getDependingAssignment())
result.add(" " + c.getSecond() + " " + c.getFirst());
}
return Joiner.on("\n").join(result);
}
@Test public void testMandatoryGroup1() throws Exception {
String actual = getParserRule("Rule: 'kw1' a1='a1' a2+='a2'* a3+='a3'+ a4+='a4'?;");
StringBuilder expected = new StringBuilder();
expected.append("a1='a1'\n");
expected.append("a2+='a2'*\n");
expected.append("a3+='a3'+\n");
expected.append("a4+='a4'?");
assertEquals(expected.toString(), actual);
}
@Test public void testMandatoryGroup2() throws Exception {
String actual = getParserRule("Rule: 'kw1' (a1='a1' a2='a2') (b1='b1'? b2='b2'?) (c1+='c1'+ c2+='c2'+) (d1+='a1'* d2+='d2'*);");
StringBuilder expected = new StringBuilder();
expected.append("a1='a1'\n");
expected.append("a2='a2'\n");
expected.append("b1='b1'?\n");
expected.append("b2='b2'?\n");
expected.append("c1+='c1'+\n");
expected.append("c2+='c2'+\n");
expected.append("d1+='a1'*\n");
expected.append("d2+='d2'*");
assertEquals(expected.toString(), actual);
}
@Test public void testOptionalGroup1() throws Exception {
String actual = getParserRule("Rule: 'kw1' (a1='a1' a2+='a2'* a3+='a3'+ a4+='a4'?)?;");
StringBuilder expected = new StringBuilder();
expected.append("a1='a1'\n");
expected.append(" MANDATORY_IF_SET a2+='a2'*\n");
expected.append(" EXCLUDE_IF_UNSET a3+='a3'+\n");
expected.append(" MANDATORY_IF_SET a3+='a3'+\n");
expected.append(" MANDATORY_IF_SET a4+='a4'?\n");
expected.append("a2+='a2'*\n");
expected.append(" EXCLUDE_IF_UNSET a1='a1'\n");
expected.append(" EXCLUDE_IF_UNSET a3+='a3'+\n");
expected.append("a3+='a3'+\n");
expected.append(" EXCLUDE_IF_UNSET a1='a1'\n");
expected.append(" MANDATORY_IF_SET a1='a1'\n");
expected.append(" MANDATORY_IF_SET a2+='a2'*\n");
expected.append(" MANDATORY_IF_SET a4+='a4'?\n");
expected.append("a4+='a4'?\n");
expected.append(" EXCLUDE_IF_UNSET a1='a1'\n");
expected.append(" EXCLUDE_IF_UNSET a3+='a3'+");
assertEquals(expected.toString(), actual);
}
@Test public void testOptionalGroup2() throws Exception {
String actual = getParserRule("Rule: 'kw1' (a1='a1' a2='a2')? (b1='b1'? b2='b2'?)? (c1+='c1'+ c2+='c2'+)? (d1+='a1'* d2+='d2'*)?;");
StringBuilder expected = new StringBuilder();
expected.append("a1='a1'\n");
expected.append(" EXCLUDE_IF_UNSET a2='a2'\n");
expected.append(" MANDATORY_IF_SET a2='a2'\n");
expected.append("a2='a2'\n");
expected.append(" EXCLUDE_IF_UNSET a1='a1'\n");
expected.append(" MANDATORY_IF_SET a1='a1'\n");
expected.append("b1='b1'?\n");
expected.append("b2='b2'?\n");
expected.append("c1+='c1'+\n");
expected.append(" EXCLUDE_IF_UNSET c2+='c2'+\n");
expected.append(" MANDATORY_IF_SET c2+='c2'+\n");
expected.append("c2+='c2'+\n");
expected.append(" EXCLUDE_IF_UNSET c1+='c1'+\n");
expected.append(" MANDATORY_IF_SET c1+='c1'+\n");
expected.append("d1+='a1'*\n");
expected.append("d2+='d2'*");
assertEquals(expected.toString(), actual);
}
@Test public void testManyGroup1() throws Exception {
String actual = getParserRule("Rule: 'kw1' (a1+='a1' a2+='a2'* a3+='a3'+ a4+='a4'?)+;");
StringBuilder expected = new StringBuilder();
expected.append("a1+='a1'\n");
expected.append(" MANDATORY_IF_SET a2+='a2'*\n");
expected.append(" MANDATORY_IF_SET a3+='a3'+\n");
expected.append(" SAME_OR_LESS a3+='a3'+\n");
expected.append(" SAME_OR_MORE a4+='a4'?\n");
expected.append("a2+='a2'*\n");
expected.append(" EXCLUDE_IF_UNSET a1+='a1'\n");
expected.append(" EXCLUDE_IF_UNSET a3+='a3'+\n");
expected.append("a3+='a3'+\n");
expected.append(" EXCLUDE_IF_UNSET a1+='a1'\n");
expected.append(" SAME_OR_MORE a1+='a1'\n");
expected.append(" MANDATORY_IF_SET a2+='a2'*\n");
expected.append(" SAME_OR_MORE a4+='a4'?\n");
expected.append("a4+='a4'?\n");
expected.append(" SAME_OR_LESS a1+='a1'\n");
expected.append(" SAME_OR_LESS a3+='a3'+");
assertEquals(expected.toString(), actual);
}
@Test public void testManyGroup2() throws Exception {
String actual = getParserRule("Rule: 'kw1' (a1='a1' a2='a2')+ (b1='b1'? b2='b2'?)+ (c1+='c1'+ c2+='c2'+)+ (d1+='a1'* d2+='d2'*)+;");
StringBuilder expected = new StringBuilder();
expected.append("a1='a1'\n");
expected.append(" SAME a2='a2'\n");
expected.append("a2='a2'\n");
expected.append(" SAME a1='a1'\n");
expected.append("b1='b1'?\n");
expected.append("b2='b2'?\n");
expected.append("c1+='c1'+\n");
expected.append(" EXCLUDE_IF_UNSET c2+='c2'+\n");
expected.append(" MANDATORY_IF_SET c2+='c2'+\n");
expected.append("c2+='c2'+\n");
expected.append(" EXCLUDE_IF_UNSET c1+='c1'+\n");
expected.append(" MANDATORY_IF_SET c1+='c1'+\n");
expected.append("d1+='a1'*\n");
expected.append("d2+='d2'*");
assertEquals(expected.toString(), actual);
}
@Test public void testOptionalManyGroup1() throws Exception {
String actual = getParserRule("Rule: 'kw1' (a1='a1' a2+='a2'* a3+='a3'+ a4+='a4'?)*;");
StringBuilder expected = new StringBuilder();
expected.append("a1='a1'\n");
expected.append(" MANDATORY_IF_SET a2+='a2'*\n");
expected.append(" MANDATORY_IF_SET a3+='a3'+\n");
expected.append(" SAME_OR_LESS a3+='a3'+\n");
expected.append(" SAME_OR_MORE a4+='a4'?\n");
expected.append("a2+='a2'*\n");
expected.append(" EXCLUDE_IF_UNSET a1='a1'\n");
expected.append(" EXCLUDE_IF_UNSET a3+='a3'+\n");
expected.append("a3+='a3'+\n");
expected.append(" EXCLUDE_IF_UNSET a1='a1'\n");
expected.append(" SAME_OR_MORE a1='a1'\n");
expected.append(" MANDATORY_IF_SET a2+='a2'*\n");
expected.append(" SAME_OR_MORE a4+='a4'?\n");
expected.append("a4+='a4'?\n");
expected.append(" SAME_OR_LESS a1='a1'\n");
expected.append(" SAME_OR_LESS a3+='a3'+");
assertEquals(expected.toString(), actual);
}
@Test public void testOptionalManyGroup2() throws Exception {
String actual = getParserRule("Rule: 'kw1' (a1='a1' a2='a2')* (b1='b1'? b2='b2'?)* (c1+='c1'+ c2+='c2'+)* (d1+='a1'* d2+='d2'*)*;");
StringBuilder expected = new StringBuilder();
expected.append("a1='a1'\n");
expected.append(" SAME a2='a2'\n");
expected.append("a2='a2'\n");
expected.append(" SAME a1='a1'\n");
expected.append("b1='b1'?\n");
expected.append("b2='b2'?\n");
expected.append("c1+='c1'+\n");
expected.append(" EXCLUDE_IF_UNSET c2+='c2'+\n");
expected.append(" MANDATORY_IF_SET c2+='c2'+\n");
expected.append("c2+='c2'+\n");
expected.append(" EXCLUDE_IF_UNSET c1+='c1'+\n");
expected.append(" MANDATORY_IF_SET c1+='c1'+\n");
expected.append("d1+='a1'*\n");
expected.append("d2+='d2'*");
assertEquals(expected.toString(), actual);
}
@Test public void testKeywords2() throws Exception {
String actual = getParserRule("Rule: 'kw1' (a1='a1' | a2+='a2') b1='b1' b2+='b2'* b3+='b3'+ b4+='b4'?;");
StringBuilder expected = new StringBuilder();
expected.append("a1='a1'\n");
expected.append(" EXCLUDE_IF_SET a2+='a2'\n");
expected.append("a2+='a2'\n");
expected.append(" EXCLUDE_IF_SET a1='a1'\n");
expected.append("b1='b1'\n");
expected.append("b2+='b2'*\n");
expected.append("b3+='b3'+\n");
expected.append("b4+='b4'?");
assertEquals(expected.toString(), actual);
}
@Test public void testKeywords3() throws Exception {
String actual = getParserRule("Rule: 'kw1' ((a1='a1' | a2+='a2') b1='b1' b2+='b2'* b3+='b3'+ b4+='b4'?)?;");
StringBuilder expected = new StringBuilder();
expected.append("a1='a1'\n");
expected.append(" EXCLUDE_IF_SET a2+='a2'\n");
expected.append(" EXCLUDE_IF_UNSET b1='b1'\n");
expected.append(" EXCLUDE_IF_UNSET b3+='b3'+\n");
expected.append("a2+='a2'\n");
expected.append(" EXCLUDE_IF_SET a1='a1'\n");
expected.append(" EXCLUDE_IF_UNSET b1='b1'\n");
expected.append(" EXCLUDE_IF_UNSET b3+='b3'+\n");
expected.append("b1='b1'\n");
expected.append(" MANDATORY_IF_SET a1='a1'\n");
expected.append(" MANDATORY_IF_SET a2+='a2'\n");
expected.append(" MANDATORY_IF_SET b2+='b2'*\n");
expected.append(" EXCLUDE_IF_UNSET b3+='b3'+\n");
expected.append(" MANDATORY_IF_SET b3+='b3'+\n");
expected.append(" MANDATORY_IF_SET b4+='b4'?\n");
expected.append("b2+='b2'*\n");
expected.append(" EXCLUDE_IF_UNSET b1='b1'\n");
expected.append(" EXCLUDE_IF_UNSET b3+='b3'+\n");
expected.append("b3+='b3'+\n");
expected.append(" MANDATORY_IF_SET a1='a1'\n");
expected.append(" MANDATORY_IF_SET a2+='a2'\n");
expected.append(" EXCLUDE_IF_UNSET b1='b1'\n");
expected.append(" MANDATORY_IF_SET b1='b1'\n");
expected.append(" MANDATORY_IF_SET b2+='b2'*\n");
expected.append(" MANDATORY_IF_SET b4+='b4'?\n");
expected.append("b4+='b4'?\n");
expected.append(" EXCLUDE_IF_UNSET b1='b1'\n");
expected.append(" EXCLUDE_IF_UNSET b3+='b3'+");
assertEquals(expected.toString(), actual);
}
}

View file

@ -0,0 +1,111 @@
/*******************************************************************************
* Copyright (c) 2015 itemis AG (http://www.itemis.eu) and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*******************************************************************************/
package org.eclipse.xtext.serializer
import com.google.inject.Inject
import org.eclipse.xtext.Grammar
import org.eclipse.xtext.junit4.InjectWith
import org.eclipse.xtext.junit4.XtextRunner
import org.eclipse.xtext.junit4.internal.XtextInjectorProvider
import org.eclipse.xtext.junit4.util.ParseHelper
import org.eclipse.xtext.junit4.validation.ValidationTestHelper
import org.eclipse.xtext.serializer.analysis.IGrammarConstraintProvider
import org.eclipse.xtext.serializer.analysis.IGrammarConstraintProvider.IFeatureInfo
import org.junit.runner.RunWith
import org.junit.Test
import org.junit.Assert
/**
* @author Moritz Eysholdt - Initial contribution and API
*/
@RunWith(XtextRunner)
@InjectWith(XtextInjectorProvider)
class GrammarConstraintProviderFeatureTest {
@Inject ValidationTestHelper validator
@Inject ParseHelper<Grammar> parser
@Inject IGrammarConstraintProvider constraintProvider
@Test def void simple() {
val actual = '''
Rule: val=ID;
'''.toFeatureInfo
val expected = '''
Rule_Rule{
val[1,1]
}
'''
Assert.assertEquals(expected, actual)
}
@Test def void optional() {
val actual = '''
Rule: {Rule} val=ID?;
'''.toFeatureInfo
val expected = '''
Rule_Rule{
val[0,1]
}
'''
Assert.assertEquals(expected, actual)
}
@Test def void multi() {
val actual = '''
Rule: val=ID+;
'''.toFeatureInfo
val expected = '''
Rule_Rule{
val[1,*]
}
'''
Assert.assertEquals(expected, actual)
}
@Test def void optionalMulti() {
val actual = '''
Rule: {Rule} val=ID*;
'''.toFeatureInfo
val expected = '''
Rule_Rule{
val[0,*]
}
'''
Assert.assertEquals(expected, actual)
}
@Test def void twoToThree() {
val actual = '''
Rule: val+=ID val+=ID val+=ID?;
'''.toFeatureInfo
val expected = '''
Rule_Rule{
val[2,3]
}
'''
Assert.assertEquals(expected, actual)
}
def String toFeatureInfo(CharSequence grammarString) {
val grammar = parser.parse('''
grammar org.eclipse.xtext.serializer.GrammarConstraintProviderFeatureTestLanguage with org.eclipse.xtext.common.Terminals
generate GrammarConstraintProviderFeatureTest "http://www.eclipse.org/2010/tmf/xtext/GrammarConstraintProviderFeatureTestLanguage"
«grammarString»
''')
validator.assertNoErrors(grammar)
val constraints = constraintProvider.getConstraints(grammar).map[constraints].flatten.toSet
return constraints.map[name + "{\n " + features.map[asString].join("\n ") + "\n}"].join("\n") + "\n"
}
def String asString(IFeatureInfo it) {
val upper = if(upperBound == IGrammarConstraintProvider.MAX) "*" else upperBound
return feature.name + "[" + lowerBound + "," + upper + "]";
}
}

View file

@ -140,12 +140,19 @@ public class GrammarConstraintProviderTest extends AbstractXtextTests {
String actual = getParserRule("Rule: (x1=ID | x2=ID) | (x3=ID | x4=ID)* | (x5=ID | x6=ID)? | (x7=ID | x8=ID)+;");
StringBuilder expected = new StringBuilder();
expected.append("Rule: Rule_Rule | Rule_null;\n");
expected.append(" Rule_Rule returns Rule: (x1=ID | x2=ID | (x3=ID | x4=ID)* | (x5=ID | x6=ID)? | (x7=ID | x8=ID)+);\n");
expected.append(" Rule_Rule returns Rule: (\n");
expected.append(" x1=ID | \n");
expected.append(" x2=ID | \n");
expected.append(" (x3=ID | x4=ID)+ | \n");
expected.append(" x5=ID | \n");
expected.append(" x6=ID | \n");
expected.append(" (x7=ID | x8=ID)+\n");
expected.append(");\n");
expected.append(" Rule_null returns null: {null};");
assertEquals(expected.toString(), actual);
}
@Test public void testEmptyAlternatives() throws Exception {
@Test @Ignore public void testEmptyAlternatives() throws Exception {
String actual = getParserRule("Rule: (x1=ID | x2=ID | 'foo') (x3=ID | x4=ID | 'foo' | 'bar');");
StringBuilder expected = new StringBuilder();
expected.append("Rule: Rule_Rule | Rule_null;\n");
@ -153,6 +160,15 @@ public class GrammarConstraintProviderTest extends AbstractXtextTests {
expected.append(" Rule_null returns null: {null};");
assertEquals(expected.toString(), actual);
}
@Test @Ignore public void testDoubleMulti() throws Exception {
String actual = getParserRule("Rule: x1=ID* x2=ID*;");
StringBuilder expected = new StringBuilder();
expected.append("Rule: Rule_Rule | Rule_null;\n");
expected.append(" Rule_Rule returns Rule: ((x1=ID+ x2=ID+) | x1=ID+ | x2=ID+);\n");
expected.append(" Rule_null returns null: {null};");
assertEquals(expected.toString(), actual);
}
@Test public void testAssignedAlternatives1() throws Exception {
String actual = getParserRule("Rule: a1=(ID|'id') a2=(ID|STRING|'bar') a3+=(ID|STRING|'bar')*;");
@ -172,7 +188,7 @@ public class GrammarConstraintProviderTest extends AbstractXtextTests {
expected.append(" a2=ID | \n");
expected.append(" a2=STRING | \n");
expected.append(" a2='bar' | \n");
expected.append(" (a3+=ID | a3+=STRING | a3+='bar')*\n");
expected.append(" (a3+=ID | a3+=STRING | a3+='bar')+\n");
expected.append(");\n");
expected.append(" Rule_null returns null: {null};");
assertEquals(expected.toString(), actual);
@ -182,7 +198,7 @@ public class GrammarConstraintProviderTest extends AbstractXtextTests {
String actual = getParserRule("Rule: {Rule} ('false' | isTrue?='true');");
StringBuilder expected = new StringBuilder();
expected.append("Rule: Rule_Rule;\n");
expected.append(" Rule_Rule returns Rule: (isTrue?='true'?);");
expected.append(" Rule_Rule returns Rule: isTrue?='true'?;");
assertEquals(expected.toString(), actual);
}
@ -341,18 +357,23 @@ public class GrammarConstraintProviderTest extends AbstractXtextTests {
}
@Test
@Ignore
public void testReturnsNullAlways() throws Exception {
String actual = getParserRule("Rule: val1=NullRule val2=ID; NullRule: 'kw1';");
StringBuilder expected = new StringBuilder();
expected.append("Rule: Rule_Rule;\n");
expected.append(" Rule_Rule returns Rule: (val1=NullRule val2=ID);");
assertEquals(expected.toString(), actual);
}
@Test
@Ignore
public void testReturnsNullSometimes() throws Exception {
String actual = getParserRule("Rule: val1=NullRule val2=ID; NullRule: 'kw1' | 'kw2' {NullRule};");
StringBuilder expected = new StringBuilder();
expected.append("Rule: Rule_Rule;\n");
expected.append(" Rule_Rule returns Rule: (val1=NullRule val2=ID);\n");
expected.append("NullRule: NullRule_NullRule | NullRule_null;\n");
expected.append(" NullRule_NullRule returns NullRule: {NullRule};\n");
expected.append(" NullRule_null returns null: {null};");
assertEquals(expected.toString(), actual);
}
}

View file

@ -73,7 +73,7 @@ public class NfaToProductionTest extends Assert {
nfa.start().followedBy("x", "y", "stop");
nfa.state("x").followedBy("stop", "x");
nfa.state("y").followedBy("stop");
assertEquals("start (x* | y?) stop", nfa2g(nfa));
assertEquals("start (x+ | y)? stop", nfa2g(nfa));
}
@Test public void testAlternative7() {