From 17e921798d43771792eae475e5ddba9fa8afa725 Mon Sep 17 00:00:00 2001 From: Sebastian Zarnekow Date: Wed, 9 Dec 2020 19:13:05 +0100 Subject: [PATCH] WIP: Fixed serialization when fragments are involved --- .../xtext/util/formallang/NfaUtil.java | 2 +- .../xtext/util/formallang/PdaUtil.java | 35 +++----- .../analysis/GrammarPDAProvider.java | 88 ++++++++++++++++++- .../serializer/analysis/SerializerPDA.java | 15 ++-- 4 files changed, 110 insertions(+), 30 deletions(-) diff --git a/org.eclipse.xtext.util/src/org/eclipse/xtext/util/formallang/NfaUtil.java b/org.eclipse.xtext.util/src/org/eclipse/xtext/util/formallang/NfaUtil.java index d3bccf454..947c0cb70 100644 --- a/org.eclipse.xtext.util/src/org/eclipse/xtext/util/formallang/NfaUtil.java +++ b/org.eclipse.xtext.util/src/org/eclipse/xtext/util/formallang/NfaUtil.java @@ -393,7 +393,7 @@ public class NfaUtil { name = "start:" + name; else if (s == nfa.getStop()) name = "stop:" + name; - names.put(name, s); + names.put(name + System.identityHashCode(s), s); } List sorted = Lists.newArrayList(names.keySet()); Collections.sort(sorted); diff --git a/org.eclipse.xtext.util/src/org/eclipse/xtext/util/formallang/PdaUtil.java b/org.eclipse.xtext.util/src/org/eclipse/xtext/util/formallang/PdaUtil.java index 06722745a..30fd20f9c 100644 --- a/org.eclipse.xtext.util/src/org/eclipse/xtext/util/formallang/PdaUtil.java +++ b/org.eclipse.xtext.util/src/org/eclipse/xtext/util/formallang/PdaUtil.java @@ -350,8 +350,7 @@ public class PdaUtil { } protected > void create(Cfg cfg, D pda, S state, E ele, Iterable followerElements, - FollowerFunction ff, Function tokens, PdaFactory fact, Map states, Map stops, - Multimap callers) { + FollowerFunction ff, Function tokens, PdaFactory fact, Set allStates, Multimap callers) { List followerStates = Lists.newArrayList(); for (E fol : followerElements) { E e; @@ -360,28 +359,24 @@ public class PdaUtil { if (root == cfg.getRoot()) followerStates.add(pda.getStop()); for (E c : callers.get(root)) { - S s = stops.get(c); - if (s == null) { - s = fact.createPop(pda, tokens.apply(c)); - stops.put(c, s); - create(cfg, pda, s, c, ff.getFollowers(c), ff, tokens, fact, states, stops, callers); + S s = fact.createPop(pda, tokens.apply(c)); + if (s != null) { + if (allStates.add(s)) { + create(cfg, pda, s, c, ff.getFollowers(c), ff, tokens, fact, allStates, callers); + } + followerStates.add(s); } - followerStates.add(s); } } else if ((e = cfg.getCall(fol)) != null) { - S s = states.get(fol); - if (s == null) { - s = fact.createPush(pda, tokens.apply(fol)); - states.put(fol, s); - create(cfg, pda, s, e, ff.getStarts(e), ff, tokens, fact, states, stops, callers); + S s = fact.createPush(pda, tokens.apply(fol)); + if (allStates.add(s)) { + create(cfg, pda, s, e, ff.getStarts(e), ff, tokens, fact, allStates, callers); } followerStates.add(s); } else { - S s = states.get(fol); - if (s == null) { - s = fact.createState(pda, tokens.apply(fol)); - states.put(fol, s); - create(cfg, pda, s, fol, ff.getFollowers(fol), ff, tokens, fact, states, stops, callers); + S s = fact.createState(pda, tokens.apply(fol)); + if (allStates.add(s)) { + create(cfg, pda, s, fol, ff.getFollowers(fol), ff, tokens, fact, allStates, callers); } followerStates.add(s); } @@ -392,10 +387,8 @@ public class PdaUtil { public > D create(Cfg cfg, FollowerFunction ff, Function element2token, PdaFactory fact) { D pda = fact.create(null, null); - Map states = Maps.newLinkedHashMap(); - Map stops = Maps.newLinkedHashMap(); Multimap callers = new CfgUtil().getCallers(cfg); - create(cfg, pda, pda.getStart(), cfg.getRoot(), ff.getStarts(cfg.getRoot()), ff, element2token, fact, states, stops, callers); + create(cfg, pda, pda.getStart(), cfg.getRoot(), ff.getStarts(cfg.getRoot()), ff, element2token, fact, new HashSet<>(), callers); return pda; } diff --git a/org.eclipse.xtext/src/org/eclipse/xtext/serializer/analysis/GrammarPDAProvider.java b/org.eclipse.xtext/src/org/eclipse/xtext/serializer/analysis/GrammarPDAProvider.java index 38dea64c1..5788f61e4 100644 --- a/org.eclipse.xtext/src/org/eclipse/xtext/serializer/analysis/GrammarPDAProvider.java +++ b/org.eclipse.xtext/src/org/eclipse/xtext/serializer/analysis/GrammarPDAProvider.java @@ -8,6 +8,8 @@ *******************************************************************************/ package org.eclipse.xtext.serializer.analysis; +import java.util.ArrayDeque; +import java.util.List; import java.util.Map; import java.util.Set; @@ -37,6 +39,7 @@ import org.eclipse.xtext.xtext.RuleFilter; import org.eclipse.xtext.xtext.RuleNames; import org.eclipse.xtext.xtext.RuleWithParameterValues; +import com.google.common.collect.FluentIterable; import com.google.common.collect.Iterables; import com.google.common.collect.Maps; import com.google.common.collect.Sets; @@ -84,10 +87,71 @@ public class GrammarPDAProvider implements IGrammarPDAProvider { protected static class ToOriginal implements PdaFactory { + private static class CompositeSerState implements ISerState { + final ISerState delegate; + final ISerState parent; + public CompositeSerState(ISerState delegate, ISerState parent) { + this.delegate = delegate; + this.parent = parent; + } + @Override + public List getFollowers() { + return delegate.getFollowers(); + } + @Override + public List getPrecedents() { + return delegate.getPrecedents(); + } + @Override + public AbstractElement getGrammarElement() { + return delegate.getGrammarElement(); + } + @Override + public SerStateType getType() { + return delegate.getType(); + } + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((delegate == null) ? 0 : delegate.hashCode()); + result = prime * result + ((parent == null) ? 0 : parent.hashCode()); + return result; + } + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + CompositeSerState other = (CompositeSerState) obj; + if (delegate == null) { + if (other.delegate != null) + return false; + } else if (!delegate.equals(other.delegate)) + return false; + if (parent == null) { + if (other.parent != null) + return false; + } else if (!parent.equals(other.parent)) + return false; + return true; + } + @Override + public String toString() { + return delegate.toString() + " with parent: " + parent; + } + } + private final SerializerPDAElementFactory delegate; private final Map pops = Maps.newHashMap(); private final Map pushs = Maps.newHashMap(); private final Map states = Maps.newHashMap(); + + private final ArrayDeque stateStack = new ArrayDeque<>(); + private final ArrayDeque callStack = new ArrayDeque<>(); public ToOriginal(SerializerPDAElementFactory delegate) { super(); @@ -102,11 +166,20 @@ public class GrammarPDAProvider implements IGrammarPDAProvider { @Override public ISerState createPop(SerializerPDA pda, AbstractElement token) { AbstractElement original = original(token); + if (GrammarUtil.isEObjectFragmentRuleCall(original)) { + if (callStack.peek() != original) { + return null; + } + } ISerState state = pops.get(original); if (state == null) { state = delegate.createPop(pda, original); pops.put(original, state); } + if (callStack.peek() == original) { + stateStack.pop(); + callStack.pop(); + } return state; } @@ -118,6 +191,10 @@ public class GrammarPDAProvider implements IGrammarPDAProvider { state = delegate.createPush(pda, original); pushs.put(original, state); } + if (GrammarUtil.isEObjectFragmentRuleCall(original)) { + stateStack.push(state); + callStack.push(original); + } return state; } @@ -125,8 +202,16 @@ public class GrammarPDAProvider implements IGrammarPDAProvider { public ISerState createState(SerializerPDA nfa, AbstractElement token) { AbstractElement original = original(token); ISerState state = states.get(original); + if (state instanceof CompositeSerState && !stateStack.isEmpty()) { + if (((CompositeSerState)state).parent != stateStack.peek()) { + state = null; + } + } if (state == null) { state = delegate.createState(nfa, original); + if (!stateStack.isEmpty()) { + state = new CompositeSerState(state, stateStack.peek()); + } states.put(original, state); } return state; @@ -146,9 +231,10 @@ public class GrammarPDAProvider implements IGrammarPDAProvider { @Override public void setFollowers(SerializerPDA nfa, ISerState owner, Iterable followers) { Set all = Sets.newLinkedHashSet(owner.getFollowers()); - Iterables.addAll(all, followers); + FluentIterable.from(followers).copyInto(all); delegate.setFollowers(nfa, owner, all); } + } private static Logger LOG = Logger.getLogger(GrammarPDAProvider.class); diff --git a/org.eclipse.xtext/src/org/eclipse/xtext/serializer/analysis/SerializerPDA.java b/org.eclipse.xtext/src/org/eclipse/xtext/serializer/analysis/SerializerPDA.java index e80a8f122..e5f9cc615 100644 --- a/org.eclipse.xtext/src/org/eclipse/xtext/serializer/analysis/SerializerPDA.java +++ b/org.eclipse.xtext/src/org/eclipse/xtext/serializer/analysis/SerializerPDA.java @@ -23,6 +23,7 @@ import org.eclipse.xtext.util.formallang.PdaFactory; import com.google.common.base.Function; import com.google.common.base.Preconditions; import com.google.common.base.Predicate; +import com.google.common.collect.FluentIterable; import com.google.common.collect.Lists; public class SerializerPDA implements Pda { @@ -88,10 +89,12 @@ public class SerializerPDA implements Pda { @Override public void setFollowers(SerializerPDA nfa, ISerState owner, Iterable followers) { - ((SerializerPDA.SerializerPDAState) owner).followers = Lists.newArrayList(followers); + List prevFollowers = (List) owner.getFollowers(); + prevFollowers.clear(); + FluentIterable.from(followers).copyInto(prevFollowers); for (ISerState follower : followers) { Preconditions.checkNotNull(follower); - ((SerializerPDA.SerializerPDAState) follower).precedents.add(owner); + ((List)follower.getPrecedents()).add(owner); } } } @@ -203,19 +206,17 @@ public class SerializerPDA implements Pda { @Override public Iterable getFollowers(ISerState state) { - return ((SerializerPDA.SerializerPDAState) state).followers; + return (Iterable) state.getFollowers(); } @Override public RuleCall getPop(ISerState state) { - SerializerPDA.SerializerPDAState s = (SerializerPDA.SerializerPDAState) state; - return s.type == SerStateType.POP ? (RuleCall) s.grammarElement : null; + return state.getType() == SerStateType.POP ? (RuleCall) state.getGrammarElement() : null; } @Override public RuleCall getPush(ISerState state) { - SerializerPDA.SerializerPDAState s = (SerializerPDA.SerializerPDAState) state; - return s.type == SerStateType.PUSH ? (RuleCall) s.grammarElement : null; + return state.getType() == SerStateType.PUSH ? (RuleCall) state.getGrammarElement() : null; } @Override