[serializer] implemented a fail-early strategy for sequencing

The backtracking algorithm of the semantic sequencer now avoids paths
that can not lead to a successful consumption of all EOBject's values.

To do so, every node of the state machine has knowledge about all
EStructuralFeatures which its subsequent nodes refer to. If an EObject
has unconsumed values within EStructuralFeatures that are not part of
this list, the algorithm will not proceed past this node.
This commit is contained in:
Moritz Eysholdt 2012-04-03 23:52:29 +02:00
parent 3ee879ab6d
commit cc4beb3446
5 changed files with 284 additions and 125 deletions

View file

@ -7,6 +7,7 @@
*******************************************************************************/
package org.eclipse.xtext.serializer.analysis;
import java.util.BitSet;
import java.util.List;
import org.eclipse.emf.ecore.EClass;
@ -24,8 +25,10 @@ import com.google.inject.ImplementedBy;
public interface ISemanticSequencerNfaProvider {
public interface ISemState {
AbstractElement getAssignedGrammarElement();
BitSet getAllFollowerFeatures();
AbstractElement getAssignedGrammarElement();
EStructuralFeature getFeature();
int getFeatureID();

View file

@ -7,6 +7,7 @@
*******************************************************************************/
package org.eclipse.xtext.serializer.analysis;
import java.util.BitSet;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
@ -36,6 +37,7 @@ import com.google.common.collect.HashMultimap;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Multimap;
import com.google.common.collect.Sets;
import com.google.inject.Inject;
import com.google.inject.Singleton;
@ -56,10 +58,6 @@ public class SemanticSequencerNfaProvider implements ISemanticSequencerNfaProvid
this.stop = stops;
}
public ISemState getStop() {
return stop;
}
public List<ISemState> getFollowers(ISemState node) {
return node.getFollowers();
}
@ -68,16 +66,21 @@ public class SemanticSequencerNfaProvider implements ISemanticSequencerNfaProvid
return start;
}
public ISemState getStop() {
return stop;
}
}
protected static class SemState implements ISemState {
protected AbstractElement assignedGrammarElement;
protected EStructuralFeature feature;
protected int featureID = -1;
protected int featureID = -2;
protected List<ISemState> followers;
protected EClass type;
protected List<AbstractElement> contentValidationNeeded;
protected BitSet allFollowerFeatures;
public SemState(EClass type, AbstractElement assignedGrammarElement) {
super();
@ -85,6 +88,12 @@ public class SemanticSequencerNfaProvider implements ISemanticSequencerNfaProvid
this.assignedGrammarElement = assignedGrammarElement;
}
public BitSet getAllFollowerFeatures() {
if (allFollowerFeatures == null)
allFollowerFeatures = new BitSet();
return allFollowerFeatures;
}
public AbstractElement getAssignedGrammarElement() {
return assignedGrammarElement;
}
@ -96,8 +105,8 @@ public class SemanticSequencerNfaProvider implements ISemanticSequencerNfaProvid
}
public int getFeatureID() {
if (featureID < 0)
featureID = type.getFeatureID(getFeature());
if (featureID < -1)
featureID = getFeature() != null ? type.getFeatureID(getFeature()) : -1;
return featureID;
}
@ -152,11 +161,53 @@ public class SemanticSequencerNfaProvider implements ISemanticSequencerNfaProvid
nfa = util.create(util.sort(synNfa, distanceMap), new SemStateFactory());
// util.sortInplace(nfa, distanceMap);
initContentValidationNeeded(type, nfa);
initRemainingFeatures(nfa.getStop(), util.inverse(nfa), Sets.<ISemState> newHashSet());
// System.out.println(new NfaFormatter().format(nfa));
cache.put(key, nfa);
return nfa;
}
protected void initContentValidationNeeded(EClass clazz, Nfa<ISemState> nfa) {
Multimap<EStructuralFeature, AbstractElement> assignments = HashMultimap.create();
Set<ISemState> states = new NfaUtil().collect(nfa);
for (ISemState state : states)
if (state.getFeature() != null)
assignments.put(state.getFeature(), state.getAssignedGrammarElement());
boolean[] validationNeeded = new boolean[clazz.getFeatureCount()];
for (EStructuralFeature feature : clazz.getEAllStructuralFeatures())
validationNeeded[clazz.getFeatureID(feature)] = isContentValidationNeeded(assignments.get(feature));
for (ISemState state : states)
if (state.getFeature() != null && validationNeeded[state.getFeatureID()])
((SemState) state).contentValidationNeeded = Lists.newArrayList(assignments.get(state.getFeature()));
else
((SemState) state).contentValidationNeeded = Collections.emptyList();
}
protected void initRemainingFeatures(ISemState state, Nfa<ISemState> inverseNfa, Set<ISemState> visited) {
BitSet features = state.getAllFollowerFeatures();
if (state.getFeature() != null) {
BitSet f = new BitSet();
f.or(features);
f.set(state.getFeatureID());
features = f;
}
for (ISemState follower : inverseNfa.getFollowers(state)) {
if (!addAll(follower.getAllFollowerFeatures(), features) && !visited.add(follower))
continue;
initRemainingFeatures(follower, inverseNfa, visited);
}
}
protected boolean addAll(BitSet to, BitSet bits) {
BitSet cpy = new BitSet();
cpy.or(to);
cpy.or(bits);
if (cpy.equals(to))
return false;
to.or(bits);
return true;
}
protected boolean isContentValidationNeeded(Collection<AbstractElement> ass) {
if (ass == null || ass.size() < 2)
return false;
@ -177,20 +228,4 @@ public class SemanticSequencerNfaProvider implements ISemanticSequencerNfaProvid
}
return false;
}
protected void initContentValidationNeeded(EClass clazz, Nfa<ISemState> nfa) {
Multimap<EStructuralFeature, AbstractElement> assignments = HashMultimap.create();
Set<ISemState> states = new NfaUtil().collect(nfa);
for (ISemState state : states)
if (state.getFeature() != null)
assignments.put(state.getFeature(), state.getAssignedGrammarElement());
boolean[] validationNeeded = new boolean[clazz.getFeatureCount()];
for (EStructuralFeature feature : clazz.getEAllStructuralFeatures())
validationNeeded[clazz.getFeatureID(feature)] = isContentValidationNeeded(assignments.get(feature));
for (ISemState state : states)
if (state.getFeature() != null && validationNeeded[state.getFeatureID()])
((SemState) state).contentValidationNeeded = Lists.newArrayList(assignments.get(state.getFeature()));
else
((SemState) state).contentValidationNeeded = Collections.emptyList();
}
}

View file

@ -25,6 +25,7 @@ import org.eclipse.xtext.nodemodel.ICompositeNode;
import org.eclipse.xtext.nodemodel.ILeafNode;
import org.eclipse.xtext.nodemodel.INode;
import org.eclipse.xtext.serializer.acceptor.SequenceFeeder;
import org.eclipse.xtext.serializer.analysis.Context2NameFunction;
import org.eclipse.xtext.serializer.analysis.ISemanticSequencerNfaProvider;
import org.eclipse.xtext.serializer.analysis.ISemanticSequencerNfaProvider.ISemState;
import org.eclipse.xtext.serializer.diagnostic.ISemanticSequencerDiagnosticProvider;
@ -74,11 +75,15 @@ public class BacktrackingSemanticSequencer extends AbstractSemanticSequencer {
return 1;
boolean o1Opt = obj.isOptional(o1.getFeatureID());
boolean o2Opt = obj.isOptional(o2.getFeatureID());
if (o1Opt && o2Opt)
return 0;
if (o1Opt)
if (o1Opt && !o2Opt)
return 1;
if (o2Opt)
if (o2Opt && !o1Opt)
return -1;
int o1Cnt = obj.getValueCount(o1.getFeatureID());
int o2Cnt = obj.getValueCount(o2.getFeatureID());
if (o1Cnt == 0 && o2Cnt > 0)
return 1;
if (o2Cnt == 0 && o1Cnt > 0)
return -1;
return 0;
}
@ -283,6 +288,20 @@ public class BacktrackingSemanticSequencer extends AbstractSemanticSequencer {
return result;
}
public boolean canEnter(ISemState state) {
for (int i = 0; i < nextIndex.length; i++)
if (i != state.getFeatureID()) {
int count = nextIndex[i];
if (count < obj.getValueCount(i)) {
if (count == 0 && obj.isOptional(i))
continue;
if (!state.getAllFollowerFeatures().get(i))
return false;
}
}
return true;
}
public int getIndex() {
return index;
}
@ -349,6 +368,7 @@ public class BacktrackingSemanticSequencer extends AbstractSemanticSequencer {
consumed.add(feature.getName() + "(" + count + ")");
}
StringBuilder result = new StringBuilder();
result.append("State: " + state + "\n");
result.append("EObject: " + EmfFormatter.objPath(obj.getEObject()) + "\n");
result.append("Remaining Mandatory Values: " + Joiner.on(", ").join(mandatory) + "\n");
result.append("Remaining Optional Values: " + Joiner.on(", ").join(optional) + "\n");
@ -399,6 +419,8 @@ public class BacktrackingSemanticSequencer extends AbstractSemanticSequencer {
TraceItem co = new TraceItem(object);
List<TraceItem> trace = new NfaUtil().backtrack(nfa, co, new NfaUtil.BacktrackHandler<ISemState, TraceItem>() {
public TraceItem handle(ISemState state, TraceItem previous) {
if (!previous.canEnter(state))
return null;
if (state.getFeature() != null) {
return previous.cloneAndConsume(state);
} else

View file

@ -83,173 +83,211 @@ public abstract class AbstractSemanticSequencerTest extends AbstractXtextTests {
@Test
@Ignore
public void testXtext() throws Exception {
// with(XtextStandaloneSetup.class);
// EObject model = getGrammarAccess().getGrammar();
// // System.out.println(EmfFormatter.objToStr(model));
// EObject ctx = GrammarUtil.findRuleForName(getGrammarAccess().getGrammar(), "AbstractToken");
// // ISemanticSequencer semSequencer = get(ISemanticSequencer.class);
// // EObject ctx = semSequencer.findContexts(model, null).iterator().next();
// ISyntacticSequencer synSeq = get(PassThroughSyntacticSequencer.class);
// IHiddenTokenSequencer hiddenSeq = get(PassThroughHiddenTokenSequencer.class);
// IRecursiveSequencer recSequencer = get(IRecursiveSequencer.class);
// ((IHiddenTokenSequencerOwner) recSequencer).setHiddenTokenSequencer(hiddenSeq);
// ((ISyntacticSequencerOwner) hiddenSeq).setSyntacticSequencer(synSeq);
// DebugSequenceAcceptor actual = new DebugSequenceAcceptor();
// recSequencer.createSequence( /* semSequencer, */ctx, model, actual, ISerializationDiagnostic.STDERR_ACCEPTOR);
// // String actual = sequenceRecursively(semSequencer, ctx, model, true);
// // System.out.println(actual);
// assertNotNull(actual);
// with(XtextStandaloneSetup.class);
// EObject model = getGrammarAccess().getGrammar();
// // System.out.println(EmfFormatter.objToStr(model));
// EObject ctx = GrammarUtil.findRuleForName(getGrammarAccess().getGrammar(), "AbstractToken");
// // ISemanticSequencer semSequencer = get(ISemanticSequencer.class);
// // EObject ctx = semSequencer.findContexts(model, null).iterator().next();
// ISyntacticSequencer synSeq = get(PassThroughSyntacticSequencer.class);
// IHiddenTokenSequencer hiddenSeq = get(PassThroughHiddenTokenSequencer.class);
// IRecursiveSequencer recSequencer = get(IRecursiveSequencer.class);
// ((IHiddenTokenSequencerOwner) recSequencer).setHiddenTokenSequencer(hiddenSeq);
// ((ISyntacticSequencerOwner) hiddenSeq).setSyntacticSequencer(synSeq);
// DebugSequenceAcceptor actual = new DebugSequenceAcceptor();
// recSequencer.createSequence( /* semSequencer, */ctx, model, actual, ISerializationDiagnostic.STDERR_ACCEPTOR);
// // String actual = sequenceRecursively(semSequencer, ctx, model, true);
// // System.out.println(actual);
// assertNotNull(actual);
}
@Test public void testSimpleGroup() throws Exception {
@Test
public void testSimpleGroup() throws Exception {
testSequence("#1 a b");
}
@Test public void testSimpleAlternative1() throws Exception {
@Test
public void testSimpleAlternative1() throws Exception {
testSequence("#2 kw2 a");
}
@Test public void testSimpleAlternative2() throws Exception {
@Test
public void testSimpleAlternative2() throws Exception {
testSequence("#2 kw2 a");
}
@Test public void testSimpleMultiplicities1() throws Exception {
@Test
public void testSimpleMultiplicities1() throws Exception {
testSequence("#3 a kw1 b kw2 c d kw3 e f");
}
@Test public void testSimpleMultiplicities2() throws Exception {
@Test
public void testSimpleMultiplicities2() throws Exception {
testSequence("#3 a kw1 kw2 c kw3 e");
}
@Test public void testSimpleMultiplicities3() throws Exception {
@Test
public void testSimpleMultiplicities3() throws Exception {
testSequence("#3 a kw1 kw2 c kw3");
}
@Test public void testGroupMultiplicities1() throws Exception {
@Test
public void testGroupMultiplicities1() throws Exception {
testSequence("#4 a kw1 b c kw2 d e f g kw3 h i j k");
}
@Test public void testGroupMultiplicities2() throws Exception {
@Test
public void testGroupMultiplicities2() throws Exception {
testSequence("#4 a kw1 b c kw2 d e kw3 h i");
}
@Test public void testGroupMultiplicities3() throws Exception {
@Test
public void testGroupMultiplicities3() throws Exception {
testSequence("#4 a kw1 b c kw2 d e kw3");
}
@Test public void testAlternativeMultiplicities1() throws Exception {
@Test
public void testAlternativeMultiplicities1() throws Exception {
testSequence("#5 a kw2 b c kw3 d kw3 e kw4 g h kw5 i kw5 j");
}
@Test public void testAlternativeMultiplicities2() throws Exception {
@Test
public void testAlternativeMultiplicities2() throws Exception {
testSequence("#5 kw1 a kw2 b kw4 g");
}
@Test public void testAlternativeMultiplicities3() throws Exception {
@Test
public void testAlternativeMultiplicities3() throws Exception {
testSequence("#5 kw1 a kw2 kw3 b kw4 kw5 g");
}
@Test public void testAlternativeMultiplicities4() throws Exception {
@Test
public void testAlternativeMultiplicities4() throws Exception {
testSequence("#5 kw1 a kw2 kw3 b kw4");
}
@Test public void testList1a() throws Exception {
@Test
public void testList1a() throws Exception {
testSequence("#6 a, b, c, d, e");
}
@Test public void testList1b() throws Exception {
@Test
public void testList1b() throws Exception {
testSequence("#6 a, b");
}
@Test public void testList1c() throws Exception {
@Test
public void testList1c() throws Exception {
testSequence("#6 a");
}
@Test public void testList2a() throws Exception {
@Test
public void testList2a() throws Exception {
testSequence("#7 a, b, c");
}
@Test public void testList2b() throws Exception {
@Test
public void testList2b() throws Exception {
testSequence("#7 a, b");
}
@Test public void testList2c() throws Exception {
@Test
public void testList2c() throws Exception {
testSequence("#7 a");
}
@Test public void testList2d() throws Exception {
@Test
public void testList2d() throws Exception {
testSequence("#7");
}
@Test public void testSingleKeyword1() throws Exception {
@Test
public void testSingleKeyword1() throws Exception {
testSequence("#10 kw1");
}
@Test public void testSingleKeyword2() throws Exception {
@Test
public void testSingleKeyword2() throws Exception {
testSequence("#10 kw2");
}
@Test public void testSingleKeyword3() throws Exception {
@Test
public void testSingleKeyword3() throws Exception {
testSequence("#10 kw3");
}
@Test public void testSingleKeyword1OrID() throws Exception {
@Test
public void testSingleKeyword1OrID() throws Exception {
testSequence("#11 kw1");
}
@Test public void testSingleKeywordOrID2() throws Exception {
@Test
public void testSingleKeywordOrID2() throws Exception {
testSequence("#11 kw2");
}
@Test public void testSingleKeywordOrID3() throws Exception {
@Test
public void testSingleKeywordOrID3() throws Exception {
testSequence("#11 kw3");
}
@Test public void testSingleKeywordOrID4() throws Exception {
@Test
public void testSingleKeywordOrID4() throws Exception {
testSequence("#11 foo");
}
@Test public void testSingleTerminals1() throws Exception {
@Test
public void testSingleTerminals1() throws Exception {
testSequence("#12 $1foo");
}
@Test public void testSingleTerminals2() throws Exception {
@Test
public void testSingleTerminals2() throws Exception {
testSequence("#12 $2foo");
}
@Test public void testSingleEnum1() throws Exception {
@Test
public void testSingleEnum1() throws Exception {
testSequence("#13 kw1");
}
@Test public void testSingleEnum2() throws Exception {
@Test
public void testSingleEnum2() throws Exception {
testSequence("#13 kw1");
}
@Test public void testSingleEnum3() throws Exception {
@Test
public void testSingleEnum3() throws Exception {
testSequence("#13 kw1");
}
@Test public void testSingleCrossReference1() throws Exception {
@Test
public void testSingleCrossReference1() throws Exception {
testSequence("#14 $1foo $1foo");
}
@Test public void testSingleCrossReference2() throws Exception {
@Test
public void testSingleCrossReference2() throws Exception {
testSequence("#14 $2foo $2foo");
}
@Test public void testSingleCrossReference3() throws Exception {
@Test
public void testSingleCrossReference3() throws Exception {
testSequence("#14 $3foo $3foo");
}
@Test public void testSingleContainmentReference1() throws Exception {
@Test
public void testSingleContainmentReference1() throws Exception {
testSequence("#15 kw1");
}
@Test public void testSingleContainmentReference2() throws Exception {
@Test
public void testSingleContainmentReference2() throws Exception {
testSequence("#15 kw2");
}
@Test public void testSingleContainmentReference3() throws Exception {
@Test
public void testSingleContainmentReference3() throws Exception {
testSequence("#15 kw3");
}
@ -271,168 +309,225 @@ public abstract class AbstractSemanticSequencerTest extends AbstractXtextTests {
testSequence("#8 kw3 a");
}
@Test public void testDependentAlternative1_a() throws Exception {
@Test
public void testDependentAlternative1_a() throws Exception {
testSequence("#19 foo1");
}
@Test public void testDependentAlternative1_b() throws Exception {
@Test
public void testDependentAlternative1_b() throws Exception {
testSequence("#19 foo kw1");
}
@Test public void testDependentAlternative2_a() throws Exception {
@Test
public void testDependentAlternative2_a() throws Exception {
testSequence("#20 foo bar");
}
@Test public void testDependentAlternative2_b() throws Exception {
@Test
public void testDependentAlternative2_b() throws Exception {
testSequence("#20 foo bar baz");
}
@Test public void testDependentAlternative2_c() throws Exception {
@Test
public void testDependentAlternative2_c() throws Exception {
testSequence("#20 foo bar kw1");
}
@Test public void testDependentAlternative2_d() throws Exception {
@Test
public void testDependentAlternative2_d() throws Exception {
testSequence("#20 foo kw1");
}
@Test public void testOptional1_a() throws Exception {
@Test
public void testOptional1_a() throws Exception {
testSequence("#21 1 2 3");
}
@Test public void testOptional1_b() throws Exception {
@Test
public void testOptional1_b() throws Exception {
testSequence("#21 0 0 1");
}
@Test public void testOptional1_c() throws Exception {
@Test
public void testOptional1_c() throws Exception {
testSequence("#21 0 0 0");
}
@Test public void testOptional1_d() throws Exception {
@Test
public void testOptional1_d() throws Exception {
testSequence("#21 0");
}
@Test public void testOptional1_e() throws Exception {
@Test
public void testOptional1_e() throws Exception {
testSequence("#21 1");
}
@Test public void testUnorderedAlternative1() throws Exception {
@Test
public void testUnorderedAlternative1() throws Exception {
testSequence("#23 1 a");
}
@Test public void testUnorderedAlternative2() throws Exception {
@Test
public void testUnorderedAlternative2() throws Exception {
testSequence("#23 a 1");
}
@Test public void testUnorderedAlternative3() throws Exception {
@Test
public void testUnorderedAlternative3() throws Exception {
testSequence("#23 kw1 a kw2 b");
}
@Test public void testUnorderedAlternative4() throws Exception {
@Test
public void testUnorderedAlternative4() throws Exception {
testSequence("#23 kw2 b kw1 a");
}
@Test public void testUnorderedAlternative5() throws Exception {
@Test
public void testUnorderedAlternative5() throws Exception {
testSequence("#23 1 a kw1 a kw2 b");
}
@Test public void testUnorderedAlternative6() throws Exception {
@Test
public void testUnorderedAlternative6() throws Exception {
testSequence("#23 kw2 b kw1 a a 1");
}
@Test public void testUnorderedAlternative7() throws Exception {
@Test
public void testUnorderedAlternative7() throws Exception {
testSequence("#23 kw2 b kw1 a a 1 c d 2 i 7 kw2 x kw1 x 8 g");
}
@Test public void testUnorderedGroup1() throws Exception {
@Test
public void testUnorderedGroup1() throws Exception {
testSequence("#24 1 a kw1 a kw2 b");
}
@Test public void testUnorderedGroup2() throws Exception {
@Test
public void testUnorderedGroup2() throws Exception {
testSequence("#24 1 a kw2 b kw1 a");
}
@Test public void testUnorderedGroup3() throws Exception {
@Test
public void testUnorderedGroup3() throws Exception {
testSequence("#24 a 1 kw1 a kw2 b");
}
@Test public void testUnorderedGroup4() throws Exception {
@Test
public void testUnorderedGroup4() throws Exception {
testSequence("#24 a 1 kw2 b kw1 a");
}
@Test public void testUnorderedGroup5() throws Exception {
@Test
public void testUnorderedGroup5() throws Exception {
testSequence("#24 kw1 a kw2 b 1 a ");
}
@Test public void testUnorderedGroup6() throws Exception {
@Test
public void testUnorderedGroup6() throws Exception {
testSequence("#24 kw2 b kw1 a 1 a");
}
@Test public void testUnorderedGroup7() throws Exception {
@Test
public void testUnorderedGroup7() throws Exception {
testSequence("#24 kw1 a kw2 b a 1");
}
@Test public void testUnorderedGroup8() throws Exception {
@Test
public void testUnorderedGroup8() throws Exception {
testSequence("#24 kw2 b kw1 a a 1");
}
@Test public void testUnorderedGroupOptional1() throws Exception {
@Test
public void testUnorderedGroupOptional1() throws Exception {
testSequence("#25 kw1 a kw2 b kw3 c");
}
@Test public void testUnorderedGroupOptional2() throws Exception {
@Test
public void testUnorderedGroupOptional2() throws Exception {
testSequence("#25 kw1 a kw2 b");
}
@Test public void testUnorderedGroupOptional3() throws Exception {
@Test
public void testUnorderedGroupOptional3() throws Exception {
testSequence("#25 kw1 a kw3 c");
}
@Test public void testUnorderedGroupOptional4() throws Exception {
@Test
public void testUnorderedGroupOptional4() throws Exception {
testSequence("#25 kw2 b kw3 c");
}
@Test public void testUnorderedGroupOptional5() throws Exception {
@Test
public void testUnorderedGroupOptional5() throws Exception {
testSequence("#25 kw2 b kw1 a");
}
@Test public void testUnorderedGroupOptional6() throws Exception {
@Test
public void testUnorderedGroupOptional6() throws Exception {
testSequence("#25 kw3 c kw1 a");
}
@Test public void testUnorderedGroupOptional7() throws Exception {
@Test
public void testUnorderedGroupOptional7() throws Exception {
testSequence("#25 kw3 c kw2 b");
}
@Test public void testUnorderedGroupBoolean1() throws Exception {
@Test
public void testUnorderedGroupBoolean1() throws Exception {
testSequence("#26 kw1 kw2");
}
@Test public void testUnorderedGroupBoolean2() throws Exception {
@Test
public void testUnorderedGroupBoolean2() throws Exception {
testSequence("#26 kw2 kw1");
}
@Test public void testUnorderedGroupBoolean3() throws Exception {
@Test
public void testUnorderedGroupBoolean3() throws Exception {
testSequence("#26 kw2 kw3");
}
@Test public void testUnorderedGroupBoolean4() throws Exception {
@Test
public void testUnorderedGroupBoolean4() throws Exception {
testSequence("#26 kw3 kw2");
}
@Test public void testUnorderedGroupBoolean5() throws Exception {
@Test
public void testUnorderedGroupBoolean5() throws Exception {
testSequence("#26 kw1 kw3");
}
@Test public void testUnorderedGroupBoolean6() throws Exception {
@Test
public void testUnorderedGroupBoolean6() throws Exception {
testSequence("#26 kw3 kw1");
}
@Test public void testUnorderedGroupBoolean7() throws Exception {
@Test
public void testUnorderedGroupBoolean7() throws Exception {
testSequence("#26 kw1 kw2 kw3");
}
@Test public void testUnorderedGroupBoolean8() throws Exception {
@Test
public void testUnorderedGroupBoolean8() throws Exception {
testSequence("#26 kw3 kw2 kw1");
}
@Test
public void testComplex1a() throws Exception {
testSequence("#27 kw1 v1 kw2 v2 kw3 v3 kw4 v4 kw5 v5 kw6 v6");
}
@Test
public void testComplex1b() throws Exception {
testSequence("#27 kw6 v6");
}
@Test
public void testComplex1c() throws Exception {
testSequence("#27 kw3 v3 kw4 v4 kw5 v5 kw6 v6");
}
}

View file

@ -18,7 +18,7 @@ Model:
x13=SingleEnum | x14=SingleCrossReference | x15=SingleContainmentReference |
x19=DependentAlternative1 | x20=DependentAlternative2 | x21=Optional | x22=Float |
x23=UnorderedAlternative | x24=UnorderedGroup | x25=UnorderedGroupOptional |
x26=UnorderedGroupBoolean;
x26=UnorderedGroupBoolean | x27=Complex1;
SimpleGroup:
"#1" val1=ID val2=ID;
@ -142,4 +142,8 @@ UnorderedGroupOptional:
"#25" {UnorderedGroupOptional} (("kw1" val1=ID)? & ("kw2" va2=ID)? & ("kw3" val3=ID)?);
UnorderedGroupBoolean:
'#26' {UnorderedGroupBoolean} (val1?='kw1'? & val2?='kw2'? & val3?='kw3'?);
'#26' {UnorderedGroupBoolean} (val1?='kw1'? & val2?='kw2'? & val3?='kw3'?);
Complex1:
'#27' {Complex1} ("kw1" val1=ID)? ("kw2" val2=ID)? (("kw3" val3+=ID) | ("kw4" val4+=ID) | ("kw5" val5+=ID) | ("kw6" val6+=ID))*;