partial parsing revisited

EContentAdapter for offset und line
This commit is contained in:
jkohnlein 2008-07-03 12:17:22 +00:00
parent 014959cf01
commit 53838b74af
22 changed files with 523 additions and 327 deletions

View file

@ -0,0 +1,47 @@
/*******************************************************************************
* Copyright (c) 2008 itemis AG (http://www.itemis.eu) and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
*******************************************************************************/
package org.eclipse.xtext.util;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
/**
* @author Jan Köhnlein - Initial contribution and API
*
*/
public class MultiMap<K,V> extends HashMap<K, List<V>>{
private static final long serialVersionUID = 28768781263L;
public void put(K key, V value) {
List<V> list = super.get(key);
if(list == null) {
list=new ArrayList<V>();
put(key, list);
}
list.add(value);
}
public boolean remove(K key, V value) {
List<V> list = super.get(key);
if(list == null) return false;
boolean result = list.remove(value);
if(list.isEmpty()) {
remove(key);
}
return result;
}
public List<V> get(K key) {
List<V> list = super.get(key);
return (list == null) ? Collections.<V>emptyList() : list;
}
}

View file

@ -6,8 +6,8 @@
<eClassifiers xsi:type="ecore:EClass" name="CompositeNode" eSuperTypes="#//AbstractNode">
<eStructuralFeatures xsi:type="ecore:EReference" name="children" upperBound="-1"
eType="#//AbstractNode" containment="true" eOpposite="#//AbstractNode/parent"/>
<eStructuralFeatures xsi:type="ecore:EAttribute" name="lookahead" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EInt"/>
<eStructuralFeatures xsi:type="ecore:EAttribute" name="lookaheadConsumed" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EInt"/>
<eStructuralFeatures xsi:type="ecore:EReference" name="lookaheadLeafNodes" upperBound="-1"
eType="#//LeafNode"/>
</eClassifiers>
<eClassifiers xsi:type="ecore:EClass" name="AbstractNode" abstract="true">
<eOperations name="length" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EInt">
@ -15,16 +15,6 @@
<details key="body" value="if (this instanceof CompositeNodeImpl) { return ParsetreeUtil.length((CompositeNodeImpl) this);} else if (this instanceof LeafNodeImpl) { return ParsetreeUtil.length((LeafNodeImpl) this);} else {return ParsetreeUtil.length((AbstractNodeImpl) this);}"/>
</eAnnotations>
</eOperations>
<eOperations name="offset" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EInt">
<eAnnotations source="http://www.eclipse.org/emf/2002/GenModel">
<details key="body" value="if (this instanceof CompositeNodeImpl) { return ParsetreeUtil.offset((CompositeNodeImpl) this);} else if (this instanceof LeafNodeImpl) { return ParsetreeUtil.offset((LeafNodeImpl) this);} else {return ParsetreeUtil.offset((AbstractNodeImpl) this);}"/>
</eAnnotations>
</eOperations>
<eOperations name="line" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EInt">
<eAnnotations source="http://www.eclipse.org/emf/2002/GenModel">
<details key="body" value="if (this instanceof CompositeNodeImpl) { return ParsetreeUtil.line((CompositeNodeImpl) this);} else if (this instanceof LeafNodeImpl) { return ParsetreeUtil.line((LeafNodeImpl) this);} else {return ParsetreeUtil.line((AbstractNodeImpl) this);}"/>
</eAnnotations>
</eOperations>
<eOperations name="serialize" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString">
<eAnnotations source="http://www.eclipse.org/emf/2002/GenModel">
<details key="body" value="if (this instanceof CompositeNodeImpl) { return ParsetreeUtil.serialize((CompositeNodeImpl) this);} else if (this instanceof LeafNodeImpl) { return ParsetreeUtil.serialize((LeafNodeImpl) this);} else {return ParsetreeUtil.serialize((AbstractNodeImpl) this);}"/>
@ -46,12 +36,19 @@
<details key="body" value="if (this instanceof CompositeNodeImpl) { return ParsetreeUtil.allSyntaxErrors((CompositeNodeImpl) this);} else if (this instanceof LeafNodeImpl) { return ParsetreeUtil.allSyntaxErrors((LeafNodeImpl) this);} else {return ParsetreeUtil.allSyntaxErrors((AbstractNodeImpl) this);}"/>
</eAnnotations>
</eOperations>
<eOperations name="endLine" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EInt">
<eAnnotations source="http://www.eclipse.org/emf/2002/GenModel">
<details key="body" value="if (this instanceof CompositeNodeImpl) { return ParsetreeUtil.endLine((CompositeNodeImpl) this);} else if (this instanceof LeafNodeImpl) { return ParsetreeUtil.endLine((LeafNodeImpl) this);} else {return ParsetreeUtil.endLine((AbstractNodeImpl) this);}"/>
</eAnnotations>
</eOperations>
<eStructuralFeatures xsi:type="ecore:EReference" name="parent" eType="#//CompositeNode"
eOpposite="#//CompositeNode/children"/>
<eStructuralFeatures xsi:type="ecore:EReference" name="grammarElement" eType="ecore:EClass http://www.eclipse.org/emf/2002/Ecore#//EObject"/>
<eStructuralFeatures xsi:type="ecore:EReference" name="element" eType="ecore:EClass http://www.eclipse.org/emf/2002/Ecore#//EObject"/>
<eStructuralFeatures xsi:type="ecore:EReference" name="syntaxError" eType="#//SyntaxError"
containment="true" eOpposite="#//SyntaxError/node"/>
<eStructuralFeatures xsi:type="ecore:EAttribute" name="offset" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EInt"/>
<eStructuralFeatures xsi:type="ecore:EAttribute" name="line" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EInt"/>
</eClassifiers>
<eClassifiers xsi:type="ecore:EClass" name="LeafNode" eSuperTypes="#//AbstractNode">
<eStructuralFeatures xsi:type="ecore:EAttribute" name="text" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/>

View file

@ -39,6 +39,7 @@ import org.eclipse.xtext.parsetree.NodeAdapter;
import org.eclipse.xtext.parsetree.NodeAdapterFactory;
import org.eclipse.xtext.parsetree.ParsetreeFactory;
import org.eclipse.xtext.parsetree.SyntaxError;
import org.eclipse.xtext.util.MultiMap;
import org.eclipse.xtext.util.Strings;
public abstract class AbstractAntlrParser extends Parser {
@ -53,6 +54,9 @@ public abstract class AbstractAntlrParser extends Parser {
protected int lastConsumedIndex = -1;
private MultiMap<Token, CompositeNode> deferredLookaheadMap = new MultiMap<Token, CompositeNode>();
private Map<Token, LeafNode> token2NodeMap = new HashMap<Token, LeafNode>();
protected AbstractAntlrParser(TokenStream input) {
super(input);
}
@ -97,8 +101,7 @@ public abstract class AbstractAntlrParser extends Parser {
leafNode.setFeature(feature);
parentNode.getChildren().add(leafNode);
lastConsumedIndex = token.getTokenIndex();
((XtextTokenStream) input).consumeLookahead();
// ((XtextTokenStream) input).decrementLookahead();
tokenConsumed(token, leafNode);
return leafNode;
}
return null;
@ -130,7 +133,8 @@ public abstract class AbstractAntlrParser extends Parser {
antlrTypeToLexerName.put(Integer.parseInt(tokenTypeId), token.substring(prefix.length()));
line = br.readLine();
}
} catch (IOException e) {
}
catch (IOException e) {
log.error(e);
throw new WrappedException(e);
}
@ -184,7 +188,8 @@ public abstract class AbstractAntlrParser extends Parser {
EList<LeafNode> leafNodes = currentNode.getLeafNodes();
if (leafNodes.isEmpty()) {
appendError(currentNode);
} else {
}
else {
appendError(leafNodes.get(leafNodes.size() - 1));
}
}
@ -268,7 +273,8 @@ public abstract class AbstractAntlrParser extends Parser {
try {
Method method = this.getClass().getMethod(antlrEntryRuleName);
current = (EObject) method.invoke(this);
} catch (InvocationTargetException ite) {
}
catch (InvocationTargetException ite) {
Throwable targetException = ite.getTargetException();
if (targetException instanceof RecognitionException) {
throw (RecognitionException) targetException;
@ -277,14 +283,17 @@ public abstract class AbstractAntlrParser extends Parser {
throw new WrappedException((Exception) targetException);
}
throw new RuntimeException(targetException);
} catch (Exception e) {
}
catch (Exception e) {
throw new WrappedException(e);
}
appendTrailingHiddenTokens(currentNode);
} finally {
}
finally {
try {
appendAllTokens();
} finally {
}
finally {
result = new ParseResult(current, currentNode);
}
}
@ -296,15 +305,28 @@ public abstract class AbstractAntlrParser extends Parser {
if (!entryRuleName.startsWith("entryRule")) {
if (!entryRuleName.startsWith("rule")) {
antlrEntryRuleName = "entryRule" + entryRuleName;
} else {
}
else {
antlrEntryRuleName = "entry" + Strings.toFirstUpper(entryRuleName);
}
} else {
}
else {
antlrEntryRuleName = entryRuleName;
}
return antlrEntryRuleName;
}
private void tokenConsumed(Token token, LeafNode leafNode) {
List<CompositeNode> nodesDecidingOnToken = deferredLookaheadMap.get(token);
for (CompositeNode nodeDecidingOnToken : nodesDecidingOnToken) {
nodeDecidingOnToken.getLookaheadLeafNodes().add(leafNode);
}
deferredLookaheadMap.remove(token);
token2NodeMap.put(token, leafNode);
((XtextTokenStream) input).consumeLookahead();
// ((XtextTokenStream) input).decrementLookahead();
}
/**
* The current lookahead is the number of tokens that have been matched by
* the parent rule to decide that the current rule has to be called.
@ -313,24 +335,39 @@ public abstract class AbstractAntlrParser extends Parser {
*/
protected void setCurrentLookahead() {
XtextTokenStream xtextTokenStream = (XtextTokenStream) input;
currentNode.setLookahead(xtextTokenStream.getCurrentLookahead());
currentNode.setLookaheadConsumed(xtextTokenStream.getLookaheadConsumedByParent());
List<Token> lookaheadTokens = xtextTokenStream.getLookaheadTokens();
for (Token lookaheadToken : lookaheadTokens) {
LeafNode leafNode = token2NodeMap.get(lookaheadToken);
if (leafNode == null) {
deferredLookaheadMap.put(lookaheadToken, currentNode);
}
else {
currentNode.getLookaheadLeafNodes().add(leafNode);
}
}
}
/**
* Sets the current lookahead to zero. See {@link
* AbstractAntlrParser#setCurrentLookahead()}
* Sets the current lookahead to zero. See
* {@link AbstractAntlrParser#setCurrentLookahead()}
*/
protected void resetLookahead() {
XtextTokenStream xtextTokenStream = (XtextTokenStream) input;
xtextTokenStream.resetLookahead();
token2NodeMap.clear();
}
protected void moveLookaheadInfo(CompositeNode source, CompositeNode target) {
target.setLookahead(source.getLookahead());
target.setLookaheadConsumed(source.getLookaheadConsumed());
source.setLookahead(0);
source.setLookaheadConsumed(0);
EList<LeafNode> sourceLookaheadLeafNodes = source.getLookaheadLeafNodes();
target.getLookaheadLeafNodes().addAll(sourceLookaheadLeafNodes);
sourceLookaheadLeafNodes.clear();
for (Token deferredLookaheadToken : deferredLookaheadMap.keySet()) {
List<CompositeNode> nodesDecidingOnToken = deferredLookaheadMap.get(deferredLookaheadToken);
while(nodesDecidingOnToken.indexOf(source) != -1) {
nodesDecidingOnToken.set(nodesDecidingOnToken.indexOf(source), target);
}
}
}
/**
@ -339,11 +376,12 @@ public abstract class AbstractAntlrParser extends Parser {
* {@link AbstractAntlrParser#setCurrentLookahead()}
*
* @see org.antlr.runtime.BaseRecognizer#match(org.antlr.runtime.IntStream,
* int, org.antlr.runtime.BitSet)
* int, org.antlr.runtime.BitSet)
*/
@Override
public void match(IntStream input, int ttype, BitSet follow) throws RecognitionException {
super.match(input, ttype, follow);
((XtextTokenStream) input).removeLastLookaheadToken();
((XtextTokenStream) input).decrementLookahead();
}

View file

@ -8,7 +8,11 @@
*******************************************************************************/
package org.eclipse.xtext.parser.antlr;
import java.util.ArrayList;
import java.util.List;
import org.antlr.runtime.CommonTokenStream;
import org.antlr.runtime.Token;
import org.antlr.runtime.TokenSource;
/**
@ -21,6 +25,8 @@ public class XtextTokenStream extends CommonTokenStream {
private int currentLookahead;
private int lookaheadConsumedByParent;
private List<Token> lookaheadTokens = new ArrayList<Token>();
public XtextTokenStream() {
super();
}
@ -41,6 +47,10 @@ public class XtextTokenStream extends CommonTokenStream {
@Override
public int LA(int i) {
currentLookahead = Math.max(currentLookahead, i);
Token lookaheadToken = LT(i);
if(!lookaheadTokens.contains(lookaheadToken)) {
lookaheadTokens.add(lookaheadToken);
}
return super.LA(i);
}
@ -51,6 +61,17 @@ public class XtextTokenStream extends CommonTokenStream {
return currentLookahead;
}
/**
* @return the lookaheadTokens
*/
public List<Token> getLookaheadTokens() {
return lookaheadTokens;
}
public void removeLastLookaheadToken() {
lookaheadTokens.remove(lookaheadTokens.size()-1);
}
/**
* @return the lookaheadConsumedByParent
*/
@ -61,6 +82,7 @@ public class XtextTokenStream extends CommonTokenStream {
public void resetLookahead() {
currentLookahead = 0;
lookaheadConsumedByParent = 0;
lookaheadTokens.clear();
}
public void decrementLookahead() {
@ -70,4 +92,5 @@ public class XtextTokenStream extends CommonTokenStream {
public void consumeLookahead() {
++lookaheadConsumedByParent;
}
}

View file

@ -24,12 +24,12 @@ public class EnclosingCompositeNodeFinder {
private int currentOffset = 0;
private int startTokenParentIndex = -1;
private List<NodeWithCachedOffset> nodesEnclosingRegion;
private List<CompositeNode> nodesEnclosingRegion;
private int offset;
private int length;
public EnclosingCompositeNodeFinder(CompositeNode parentNode, int offset, int length) {
nodesEnclosingRegion = new ArrayList<NodeWithCachedOffset>();
nodesEnclosingRegion = new ArrayList<CompositeNode>();
this.offset = offset;
this.length = length;
findRegion(parentNode);
@ -38,13 +38,13 @@ public class EnclosingCompositeNodeFinder {
/**
* @return the nodesEnclosingRegion
*/
public List<NodeWithCachedOffset> getNodesEnclosingRegion() {
public List<CompositeNode> getNodesEnclosingRegion() {
return nodesEnclosingRegion;
}
private boolean findRegion(CompositeNode parentNode) {
boolean isLookingForStartToken = startTokenParentIndex == -1;
nodesEnclosingRegion.add(new NodeWithCachedOffset(currentOffset, parentNode));
nodesEnclosingRegion.add(parentNode);
int currentParentIndex = nodesEnclosingRegion.size() - 1;
EList<AbstractNode> children = parentNode.getChildren();
for (AbstractNode child : children) {

View file

@ -28,7 +28,7 @@ public class NodeWithCachedOffset {
/**
* @return the offset
*/
public int getChachedOffset() {
public int getCachedOffset() {
return offset;
}

View file

@ -34,21 +34,21 @@ public class PartialParsingPointers {
private int length;
private int offset;
private List<CompositeNode> validReplaceRootNodes;
private List<NodeWithCachedOffset> nodesEnclosingChangeRegion;
private List<CompositeNode> nodesEnclosingChangeRegion;
public PartialParsingPointers(CompositeNode rootNode, int offset, int length,
List<CompositeNode> validReplaceRootNodes, List<NodeWithCachedOffset> nodesEnclosingChangeRegion) {
List<CompositeNode> validReplaceRootNodes, List<CompositeNode> nodesEnclosingRegion) {
if (validReplaceRootNodes == null || validReplaceRootNodes.isEmpty()) {
throw new IllegalArgumentException("validReplaceRootNodes cannot be empty");
}
if (nodesEnclosingChangeRegion == null || nodesEnclosingChangeRegion.isEmpty()) {
if (nodesEnclosingRegion == null || nodesEnclosingRegion.isEmpty()) {
throw new IllegalArgumentException("nodesEnclosingChangeRegion cannot be empty");
}
this.rootNode = rootNode;
this.offset = offset;
this.length = length;
this.validReplaceRootNodes = validReplaceRootNodes;
this.nodesEnclosingChangeRegion = nodesEnclosingChangeRegion;
this.nodesEnclosingChangeRegion = nodesEnclosingRegion;
}
public String findReparseRegion() {
@ -92,12 +92,12 @@ public class PartialParsingPointers {
public EObject findASTReplaceElement(CompositeNode replaceRootNode) {
boolean foundReplaceNode = false;
for (int i = 0; i < nodesEnclosingChangeRegion.size(); ++i) {
CompositeNode nodeEnclosingRegion = (CompositeNode) nodesEnclosingChangeRegion.get(i).getNode();
CompositeNode nodeEnclosingRegion = nodesEnclosingChangeRegion.get(i);
if (nodeEnclosingRegion == replaceRootNode) {
foundReplaceNode = true;
}
if (foundReplaceNode) {
EObject currentASTElement = nodesEnclosingChangeRegion.get(i).getNode().getElement();
EObject currentASTElement = nodesEnclosingChangeRegion.get(i).getElement();
if (currentASTElement != null) {
return currentASTElement;
}
@ -107,7 +107,7 @@ public class PartialParsingPointers {
// In this case, there must not be any composite nodes with
// multiple composite children on the way down.
return NodeUtil.getASTElementForRootNode((CompositeNode) nodesEnclosingChangeRegion.get(
nodesEnclosingChangeRegion.size() - 1).getNode());
nodesEnclosingChangeRegion.size() - 1));
}
public String findASTContainmentFeatureName() {

View file

@ -101,7 +101,7 @@ public class PartialParsingUtil {
public static String insertChangeIntoReplaceRegion(CompositeNode replaceRootNode, int originalOffset,
int originalLength, String changedRegion) {
String originalRegion = replaceRootNode.serialize();
int changeOffset = originalOffset - replaceRootNode.offset();
int changeOffset = originalOffset - replaceRootNode.getOffset();
StringBuffer reparseRegion = new StringBuffer();
reparseRegion.append(originalRegion.substring(0, changeOffset));
reparseRegion.append(changedRegion);
@ -110,7 +110,7 @@ public class PartialParsingUtil {
}
public static PartialParsingPointers calculatePartialParsingPointers(CompositeNode rootNode, int offset, int length) {
List<NodeWithCachedOffset> nodesEnclosingRegion = collectNodesEnclosingChangeRegion(rootNode, offset, length);
List<CompositeNode> nodesEnclosingRegion = collectNodesEnclosingChangeRegion(rootNode, offset, length);
List<CompositeNode> validReplaceRootNodes = internalFindValidReplaceRootNodeForChangeRegion(
nodesEnclosingRegion, offset, length);
if (validReplaceRootNodes.isEmpty()) {
@ -122,11 +122,30 @@ public class PartialParsingUtil {
/**
* Collects a list of all nodes containing the change region
*/
private static List<NodeWithCachedOffset> collectNodesEnclosingChangeRegion(CompositeNode parent, int offset,
int length) {
EnclosingCompositeNodeFinder enclosingCompositeNodeFinder = new EnclosingCompositeNodeFinder(parent, offset,
length);
return enclosingCompositeNodeFinder.getNodesEnclosingRegion();
private static List<CompositeNode> collectNodesEnclosingChangeRegion(CompositeNode parent, int offset, int length) {
List<CompositeNode> nodesEnclosingRegion = new ArrayList<CompositeNode>();
if (nodeEnclosesRegion(parent, offset, length)) {
collectNodesEnclosingChangeRegion(parent, offset, length, nodesEnclosingRegion);
}
return nodesEnclosingRegion;
}
private static void collectNodesEnclosingChangeRegion(CompositeNode parent, int offset, int length,
List<CompositeNode> nodesEnclosingRegion) {
nodesEnclosingRegion.add(parent);
for (AbstractNode child : parent.getChildren()) {
if (child instanceof CompositeNode) {
CompositeNode childCompositeNode = (CompositeNode) child;
if (nodeEnclosesRegion(childCompositeNode, offset, length)) {
collectNodesEnclosingChangeRegion(childCompositeNode, offset, length, nodesEnclosingRegion);
break;
}
}
}
}
private static boolean nodeEnclosesRegion(CompositeNode node, int offset, int length) {
return node.getOffset() <= offset && node.getOffset() + node.length() >= offset + length;
}
/**
@ -141,167 +160,72 @@ public class PartialParsingUtil {
* @return
*/
private static List<CompositeNode> internalFindValidReplaceRootNodeForChangeRegion(
List<NodeWithCachedOffset> nodesEnclosingRegion, int offset, int length) {
List<NodeWithCachedOffset> lookaheadNodes = new ArrayList<NodeWithCachedOffset>();
int numConsumedLookaheadTokens = 0;
List<CompositeNode> nodesEnclosingRegion, int offset, int length) {
List<LeafNode> lookaheadNodes = new ArrayList<LeafNode>();
List<CompositeNode> validReplaceRootNodes = new ArrayList<CompositeNode>();
for (NodeWithCachedOffset node : nodesEnclosingRegion) {
List<NodeWithCachedOffset> parentsLookaheadNodes = getParentsLookaheadNodes(node);
boolean lookaheadChanged = false;
for (CompositeNode node : nodesEnclosingRegion) {
// EList<LeafNode> parentsLookaheadNodes = ((CompositeNode)
// node.getNode()).getLookaheadLeafNodes();
EList<LeafNode> parentsLookaheadNodes = node.getLookaheadLeafNodes();
if (!parentsLookaheadNodes.isEmpty()) {
mergeLookaheadNodes(lookaheadNodes, parentsLookaheadNodes);
}
if (((CompositeNode) node.getNode()).getLookaheadConsumed() > 0) {
// parent has consumed lookahead tokens.
numConsumedLookaheadTokens += ((CompositeNode) node.getNode()).getLookaheadConsumed();
if (numConsumedLookaheadTokens == lookaheadNodes.size()) {
// parent has consumed all current lookahead tokens
NodeWithCachedOffset leafNode = lookaheadNodes.get(numConsumedLookaheadTokens - 1);
if (nodeIsBeforeRegion(leafNode, offset)) {
// last lookahead token is before changed region
//NodeUtil.dumpCompositeNodeInfo("Possible entry node: "
// , node);
validReplaceRootNodes.add((CompositeNode) node.getNode());
int index = lookaheadNodes.indexOf(parentsLookaheadNodes.get(0));
if (index > 0) {
// remove lookahead nodes common with grandpa
while (lookaheadNodes.size() > index) {
lookaheadNodes.remove(index);
}
}
lookaheadNodes.addAll(parentsLookaheadNodes);
lookaheadChanged = true;
}
if (lookaheadChanged) {
LeafNode lastLookaheadLeafNode = lookaheadNodes.get(lookaheadNodes.size() - 1);
if (nodeIsBeforeRegion(lastLookaheadLeafNode, offset)) {
// last lookahead token is before changed region
// and parent has consumed all current lookahead tokens
// NodeUtil.dumpCompositeNodeInfo("Possible entry node: "
// , node);
validReplaceRootNodes.add(node);
lookaheadChanged = false;
}
}
// List<NodeWithCachedOffset> lookaheadNodes = new
// ArrayList<NodeWithCachedOffset>();
// int numConsumedLookaheadTokens = 0;
// List<CompositeNode> validReplaceRootNodes = new
// ArrayList<CompositeNode>();
//
// for (NodeWithCachedOffset node : nodesEnclosingRegion) {
// List<NodeWithCachedOffset> parentsLookaheadNodes =
// getParentsLookaheadNodes(node);
// if (!parentsLookaheadNodes.isEmpty()) {
// mergeLookaheadNodes(lookaheadNodes, parentsLookaheadNodes);
// }
// if (((CompositeNode) node.getNode()).getLookaheadConsumed() > 0)
// {
// // parent has consumed lookahead tokens.
// numConsumedLookaheadTokens += ((CompositeNode)
// node.getNode()).getLookaheadConsumed();
// if (numConsumedLookaheadTokens == lookaheadNodes.size()) {
// // parent has consumed all current lookahead tokens
// NodeWithCachedOffset leafNode =
// lookaheadNodes.get(numConsumedLookaheadTokens - 1);
// if (nodeIsBeforeRegion(leafNode, offset)) {
// // last lookahead token is before changed region
// //NodeUtil.dumpCompositeNodeInfo("Possible entry node: "
// // , node);
// validReplaceRootNodes.add((CompositeNode) node.getNode());
// }
// }
// }
}
return validReplaceRootNodes;
}
private static void mergeLookaheadNodes(List<NodeWithCachedOffset> lookaheadNodes,
List<NodeWithCachedOffset> parentsLookaheadNodes) {
NodeWithCachedOffset firstParentLookaheadNode = parentsLookaheadNodes.get(0);
int index;
for(index=0; index < lookaheadNodes.size(); ++index) {
if(lookaheadNodes.get(index).getNode().equals(firstParentLookaheadNode.getNode())) {
break;
}
}
if (index < lookaheadNodes.size()) {
// remove lookahead nodes common with grandpa
while (lookaheadNodes.size() > index) {
lookaheadNodes.remove(index);
}
}
lookaheadNodes.addAll(parentsLookaheadNodes);
private static boolean nodeIsBeforeRegion(LeafNode node, int offset) {
return node.getOffset() + node.length() <= offset;
}
private static boolean nodeIsBeforeRegion(NodeWithCachedOffset leafNode, int offset) {
return leafNode.getChachedOffset() + leafNode.getNode().length() <= offset;
}
private static List<NodeWithCachedOffset> getParentsLookaheadNodes(NodeWithCachedOffset child) {
int lookaheadFromParent = ((CompositeNode) child.getNode()).getLookahead();
if (lookaheadFromParent != 0) {
List<NodeWithCachedOffset> lookaheadNodes = new ArrayList<NodeWithCachedOffset>();
int consumedByParent = ((CompositeNode) child.getNode()).getLookaheadConsumed();
if (consumedByParent > 0) {
// some lookahead nodes are consumed by parent
NodeWithCachedOffset tempLeaf = child;
for (int i = 0; i < consumedByParent; ++i) {
tempLeaf = previousUnhiddenLeaf(tempLeaf.getNode(), tempLeaf.getChachedOffset());
lookaheadNodes.add(tempLeaf);
}
}
if (lookaheadFromParent - consumedByParent > 0) {
// remaining lookahead nodes consumed by child
NodeWithCachedOffset tempLeaf = child;
for (int i = 0; i < lookaheadFromParent - consumedByParent; ++i) {
tempLeaf = nextUnhiddenLeaf(tempLeaf.getNode(), tempLeaf.getChachedOffset());
lookaheadNodes.add(tempLeaf);
}
}
return lookaheadNodes;
}
else {
return Collections.<NodeWithCachedOffset> emptyList();
}
}
private static NodeWithCachedOffset nextUnhiddenLeaf(AbstractNode node, int currentOffset) {
if (node instanceof CompositeNode) {
NodeWithCachedOffset firstLeaf = firstLeaf((CompositeNode) node, currentOffset);
if (firstLeaf != null) {
return firstLeaf;
}
}
CompositeNode parent = node.getParent();
EList<AbstractNode> siblings = parent.getChildren();
int childIndex = siblings.indexOf(node);
while (++childIndex < siblings.size()) {
AbstractNode sibling = siblings.get(childIndex);
if (sibling instanceof LeafNode) {
if (!((LeafNode) sibling).isHidden()) {
return new NodeWithCachedOffset(currentOffset, sibling);
}
currentOffset += sibling.length();
}
else if (sibling instanceof CompositeNode) {
return nextUnhiddenLeaf(sibling, currentOffset);
}
}
return nextUnhiddenLeaf(parent, currentOffset);
}
private static NodeWithCachedOffset firstLeaf(CompositeNode node, int currentOffset) {
EList<AbstractNode> children = node.getChildren();
for (AbstractNode child : children) {
if (child instanceof LeafNode) {
if (!((LeafNode) child).isHidden()) {
return new NodeWithCachedOffset(currentOffset, child);
}
currentOffset += child.length();
}
else if (child instanceof CompositeNode) {
NodeWithCachedOffset leafFromChild = firstLeaf((CompositeNode) child, currentOffset);
if (leafFromChild != null) {
return leafFromChild;
}
}
}
return null;
}
private static NodeWithCachedOffset previousUnhiddenLeaf(AbstractNode node, int currentOffset) {
CompositeNode parent = node.getParent();
EList<AbstractNode> siblings = parent.getChildren();
int childIndex = siblings.indexOf(node);
while (--childIndex >= 0) {
AbstractNode sibling = siblings.get(childIndex);
if (sibling instanceof LeafNode) {
currentOffset -= sibling.length();
if(!((LeafNode) sibling).isHidden()) {
return new NodeWithCachedOffset(currentOffset, sibling);
}
}
else {
NodeWithCachedOffset leafFromComposite = lastUnhiddenLeaf((CompositeNode) sibling, currentOffset);
if (leafFromComposite != null) {
return leafFromComposite;
}
}
}
return previousUnhiddenLeaf(parent, currentOffset);
}
private static NodeWithCachedOffset lastUnhiddenLeaf(CompositeNode node, int currentOffset) {
EList<AbstractNode> children = node.getChildren();
for (int i = children.size() - 1; i >= 0; --i) {
AbstractNode child = children.get(i);
if (child instanceof LeafNode) {
currentOffset -= child.length();
if(!((LeafNode) child).isHidden()) {
return new NodeWithCachedOffset(currentOffset, child);
}
}
else if (child instanceof CompositeNode) {
NodeWithCachedOffset leafFromChild = lastUnhiddenLeaf((CompositeNode) child, currentOffset);
if (leafFromChild != null) {
return leafFromChild;
}
}
}
return null;
}
}
}

View file

@ -0,0 +1,123 @@
/*******************************************************************************
* Copyright (c) 2008 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.parsetree;
import org.eclipse.emf.common.notify.Notification;
import org.eclipse.emf.common.util.EList;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.util.EContentAdapter;
/**
* @author Jan Köhnlein - Initial contribution and API
*
*/
public class NodeContentAdapter extends EContentAdapter {
@Override
public void notifyChanged(Notification notification) {
super.notifyChanged(notification);
Object notifier = notification.getNotifier();
if (notifier instanceof CompositeNode) {
CompositeNode parent = (CompositeNode) notifier;
Object feature = notification.getFeature();
if (ParsetreePackage.Literals.COMPOSITE_NODE__CHILDREN.equals(feature)) {
AbstractNode child = (AbstractNode) notification.getNewValue();
int eventType = notification.getEventType();
int position = notification.getPosition();
switch (eventType) {
case Notification.ADD:
if (position == 0) {
updateOffsetAndLine(child, new NodeInfo(parent.getOffset(), parent.getLine()));
}
else {
AbstractNode predecessor = parent.getChildren().get(position - 1);
updateOffsetAndLine(child, new NodeInfo((predecessor.getOffset() + predecessor.length()), predecessor.endLine()));
}
break;
case Notification.REMOVE:
if (position == 0) {
updateOffsetAndLine(parent, new NodeInfo(parent.getOffset(), parent.getLine()));
}
else {
AbstractNode successor = parent.getChildren().get(position);
updateOffsetAndLine(successor, new NodeInfo(child.getOffset(), child.getLine()));
}
break;
case Notification.ADD_MANY:
case Notification.MOVE:
case Notification.REMOVE_MANY:
updateOffsetAndLine(parent, new NodeInfo(parent.getOffset(), parent.getLine()));
break;
default:
break;
}
}
}
}
@Override
protected void setTarget(EObject target) {
if (target instanceof AbstractNode) {
AbstractNode targetNode = (AbstractNode) target;
CompositeNode parent = targetNode.getParent();
if (parent != null) {
EList<AbstractNode> siblings = parent.getChildren();
int index = siblings.indexOf(target);
if (index == 0) {
updateOffsetAndLine(targetNode, new NodeInfo(parent.getOffset(), parent.getLine()));
}
else {
AbstractNode predecessor = siblings.get(index - 1);
updateOffsetAndLine(targetNode, new NodeInfo((predecessor.getOffset() + predecessor.length()), predecessor.endLine()));
}
}
else {
updateOffsetAndLine(targetNode, new NodeInfo(0, 1));
}
}
}
static class NodeInfo {
int offset;
int line;
public NodeInfo(int offset, int line) {
this.offset = offset;
this.line = line;
}
}
protected NodeInfo updateOffsetAndLineInContents(AbstractNode node, NodeInfo info) {
node.setOffset(info.offset);
node.setLine(info.line);
if (node instanceof LeafNode) {
info.offset += node.length();
info.line = node.endLine();
}
if (node instanceof CompositeNode) {
for (AbstractNode child : ((CompositeNode) node).getChildren()) {
info = updateOffsetAndLineInContents(child, info);
}
}
return info;
}
protected AbstractNode updateOffsetAndLine(AbstractNode node, NodeInfo info) {
updateOffsetAndLineInContents(node, info);
CompositeNode parent = node.getParent();
if (parent != null) {
EList<AbstractNode> siblings = parent.getChildren();
int index = siblings.indexOf(node);
for (int i = index + 1; i < siblings.size(); ++i) {
info = updateOffsetAndLineInContents(siblings.get(i), info);
}
}
return node;
}
}

View file

@ -101,8 +101,13 @@ public class NodeUtil {
name = grammarElement.getClass().getSimpleName();
}
String astElementAsString = (node.getElement() == null) ? "null" : node.getElement().eClass().getName();
System.out.println(indent + node.getLookahead() + " " + node.getLookaheadConsumed() + " " + name + " : " + node.serialize()
+ " -> " + astElementAsString);
System.out.print(indent + name + " : " + node.serialize()
+ " -> " + astElementAsString + " la={ ");
for (LeafNode lookaheadNode : node.getLookaheadLeafNodes()) {
System.out.print("\""+ lookaheadNode.getText() +"\" ");
}
System.out.print(" (" + node.getOffset() + ", " + node.length() + ")");
System.out.println("}");
}
}

View file

@ -41,20 +41,6 @@ public class ParsetreeUtil {
+ abstractParserNode.eClass().getName());
}
public static int offset(AbstractNodeImpl abstractParserNode) {
checkArgument(abstractParserNode);
AbstractNode rootContainer = (AbstractNode) EcoreUtil.getRootContainer(abstractParserNode);
if (rootContainer == abstractParserNode) {
return 0;
}
EList<LeafNode> leafNodes = rootContainer.getLeafNodes(abstractParserNode);
int offset = 0;
for (LeafNode leafNode : leafNodes) {
offset += leafNode.length();
}
return offset;
}
private static void checkArgument(AbstractNodeImpl abstractParserNode) {
int classifierID = abstractParserNode.eClass().getClassifierID();
if (classifierID != ParsetreePackage.COMPOSITE_NODE && classifierID != ParsetreePackage.LEAF_NODE) {
@ -79,6 +65,18 @@ public class ParsetreeUtil {
}
return line;
}
public static int endLine(AbstractNodeImpl _this) {
int line = _this.getLine();
String text = _this.serialize();
char[] charArray = text.toCharArray();
for (char c : charArray) {
// TODO handle os specific newlines
if (c == '\n' || c == '\r')
line++;
}
return line;
}
public static String serialize(AbstractNodeImpl _this) {
checkArgument(_this);

View file

@ -25,6 +25,7 @@ import org.eclipse.xtext.parsetree.AbstractNode;
import org.eclipse.xtext.parsetree.CompositeNode;
import org.eclipse.xtext.parsetree.IParseTreeConstructor;
import org.eclipse.xtext.parsetree.NodeAdapter;
import org.eclipse.xtext.parsetree.NodeContentAdapter;
import org.eclipse.xtext.service.Inject;
/**
@ -58,6 +59,13 @@ public class XtextResource extends ResourceImpl {
int documentGrowth = length - rootNode.length();
int originalLength = length - documentGrowth;
parseResult = parser.reparse(rootNode, offset, originalLength, change);
if (parseResult != null && parseResult.getRootNode() != rootNode) {
addNodeContentAdapter();
}
}
private void addNodeContentAdapter() {
parseResult.getRootNode().eAdapters().add(new NodeContentAdapter());
}
@Override
@ -69,6 +77,7 @@ public class XtextResource extends ResourceImpl {
if (rootElement != null) {
getContents().add(rootElement);
}
addNodeContentAdapter();
}
}

View file

@ -31,8 +31,8 @@ public class ParseErrorHandlingTest extends AbstractGeneratorTest {
EList<SyntaxError> errors = NodeUtil.getRootNode(root).allSyntaxErrors();
assertEquals(1,errors.size());
assertEquals("%", ((LeafNode)errors.get(0).getNode()).getText());
assertEquals(1, errors.get(0).getNode().line());
assertEquals(15, errors.get(0).getNode().offset());
assertEquals(1, errors.get(0).getNode().getLine());
assertEquals(15, errors.get(0).getNode().getOffset());
assertEquals(1, errors.get(0).getNode().length());
assertEquals(1, errors.size());
}
@ -41,8 +41,8 @@ public class ParseErrorHandlingTest extends AbstractGeneratorTest {
EObject root = getModel("import 'holla' foo returns x::y::Z : name=ID;");
EList<SyntaxError> errors = NodeUtil.getRootNode(root).allSyntaxErrors();
assertEquals("::", ((LeafNode)errors.get(0).getNode()).getText());
assertEquals(1, errors.get(0).getNode().line());
assertEquals(31, errors.get(0).getNode().offset());
assertEquals(1, errors.get(0).getNode().getLine());
assertEquals(31, errors.get(0).getNode().getOffset());
assertEquals(2, errors.get(0).getNode().length());
assertEquals(1, errors.size());
}

View file

@ -19,7 +19,7 @@ import org.eclipse.xtext.util.EmfStructureComparator;
*/
public abstract class AbstractPartialParserTest extends AbstractGeneratorTest {
protected static final boolean DEBUG = false;
protected static final boolean DEBUG = true;
protected EmfStructureComparator comparator;
@Override

View file

@ -1,52 +0,0 @@
/*******************************************************************************
* Copyright (c) 2008 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.parser;
import static org.eclipse.xtext.parsetree.NodeUtil.dumpCompositeNodes;
import static org.eclipse.xtext.parsetree.NodeUtil.getCompositeChildren;
import java.util.List;
import org.eclipse.xtext.parsetree.CompositeNode;
import org.eclipse.xtext.testlanguages.LookaheadLanguageStandaloneSetup;
import org.eclipse.xtext.tests.AbstractGeneratorTest;
/**
* @author Jan Köhnlein - Initial contribution and API
*
*/
public class LookaheadTest extends AbstractGeneratorTest {
/* (non-Javadoc)
* @see org.eclipse.xtext.tests.AbstractGeneratorTest#setUp()
*/
@Override
protected void setUp() throws Exception {
super.setUp();
with(LookaheadLanguageStandaloneSetup.class);
}
public void testLookahead() throws Exception {
CompositeNode rootNode = getRootNode("bar a foo bar c b d foo bar b c");
dumpCompositeNodes("", rootNode);
assertEquals(0, rootNode.getLookahead());
List<CompositeNode> alts = getCompositeChildren(rootNode);
assertEquals(1, alts.get(0).getLookahead());
assertEquals(1, alts.get(1).getLookahead());
assertEquals(1, alts.get(2).getLookahead());
assertEquals(1, getCompositeChildren(alts.get(0)).get(0).getLookahead());
CompositeNode lookahead0 = getCompositeChildren(alts.get(1)).get(0);
assertEquals(3, lookahead0.getLookahead());
assertEquals(0, getCompositeChildren(lookahead0).get(0).getLookahead());
CompositeNode lookahead3 = getCompositeChildren(alts.get(2)).get(0);
assertEquals(3, lookahead3.getLookahead());
assertEquals(0, getCompositeChildren(lookahead3).get(0).getLookahead());
}
}

View file

@ -25,9 +25,9 @@ public class PartialParserReplaceTest extends AbstractPartialParserTest {
public void testExpression() throws Exception {
with(SimpleExpressionsStandaloneSetup.class);
String model = "(a+b+c)*(c/d)";
replaceAndReparse(model, 2, 2, "+hugo+egon", "a+hugo+egon+c");
replaceAndReparse(model, 2, 2, "+hugo+egon", "(a+hugo+egon+c)");
replaceAndReparse(model, 8, 5, "egon", "egon");
replaceAndReparse(model, 1, 2, "", "b+c");
replaceAndReparse(model, 1, 2, "", "(b+c)");
replaceAndReparse(model, 6, 3, "*", "(a+b+c*c/d)");
replaceAndReparse(model, 3, 1, "(x+y+z)", "(x+y+z)");
@ -40,7 +40,7 @@ public class PartialParserReplaceTest extends AbstractPartialParserTest {
public void testLookahead() throws Exception {
with(LookaheadLanguageStandaloneSetup.class);
String model = "foo bar b c";
replaceAndReparse(model, 10, 1, "d", " d");
replaceAndReparse(model, 10, 1, "d", "foo bar b d");
replaceAndReparse(model, 8, 1, "b", "foo bar b c");
replaceAndReparse(model, 0, model.length(), "", "");
}

View file

@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2008 itemis AG (http://www.itemis.eu) and others.
* Copyright (c) NUM_ELEMENTS8 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
@ -18,15 +18,15 @@ import org.eclipse.xtext.testlanguages.SimpleExpressionsStandaloneSetup;
*/
public class PartialParsingPerformanceTest extends AbstractPartialParserTest {
public void testPerformance() throws Exception {
int magicnumber = 10;
private static final int NUM_ELEMENTS = 10;
public void testExpression() throws Exception {
with(SimpleExpressionsStandaloneSetup.class);
StringBuffer modelBuffer = new StringBuffer();
for(int i=0; i<magicnumber; ++i) {
for(int i=0; i<NUM_ELEMENTS; ++i) {
modelBuffer.append("(a+(b*");
}
modelBuffer.append("c");
for(int i=0; i<magicnumber; ++i) {
for(int i=0; i<NUM_ELEMENTS; ++i) {
modelBuffer.append(")+d)");
}
String model = modelBuffer.toString();
@ -35,4 +35,29 @@ public class PartialParsingPerformanceTest extends AbstractPartialParserTest {
assertTrue(reparse.getParseErrors() == null || reparse.getParseErrors().isEmpty());
}
// public void testReference() throws Exception {
// with(ReferenceGrammarStandaloneSetup.class);
// StringBuffer modelBuffer = new StringBuffer();
// modelBuffer.append("spielplatz 17 {\n");
// for(int i=0; i<NUM_ELEMENTS; ++i) {
// modelBuffer.append(" kind ( Herbert");
// modelBuffer.append(i);
// modelBuffer.append(" 11 )\n");
// }
// for(int i=0; i<NUM_ELEMENTS; ++i) {
// modelBuffer.append(" erwachsener ( Hugo");
// modelBuffer.append(i);
// modelBuffer.append(" 111 )\n");
// }
// modelBuffer.append(" erwachsener ( Sven 112 )\n");
// for(int i=0; i<NUM_ELEMENTS; ++i) {
// modelBuffer.append(" spielzeug ( Schaufel GRÜN )\n");
// }
// modelBuffer.append("}\n");
// String model = modelBuffer.toString();
// CompositeNode rootNode = getRootNode(model);
// IParseResult reparse = PartialParsingUtil.reparse(getParser(), rootNode, model.indexOf("Sven"), 4, "Peter");
// assertTrue(reparse.getParseErrors() == null || reparse.getParseErrors().isEmpty());
// }
}

View file

@ -18,6 +18,7 @@ import org.eclipse.xtext.parser.impl.PartialParsingPointers;
import org.eclipse.xtext.parser.impl.PartialParsingUtil;
import org.eclipse.xtext.parsetree.CompositeNode;
import org.eclipse.xtext.testlanguages.LookaheadLanguageStandaloneSetup;
import org.eclipse.xtext.testlanguages.ReferenceGrammarStandaloneSetup;
import org.eclipse.xtext.testlanguages.SimpleExpressionsStandaloneSetup;
/**
@ -29,25 +30,27 @@ public class PartialParsingPointerTest extends AbstractPartialParserTest {
public void testExpression() throws Exception {
with(SimpleExpressionsStandaloneSetup.class);
String model = "(a+b+c)*(c/d)";
PartialParsingPointers parsingPointers = calculatePartialParsingPointers(model, 1, 1);
checkParseRegionPointers(parsingPointers, "a+b+c", "ActionImpl", "Addition", "Op", "Op", "values");
PartialParsingPointers parsingPointers;
parsingPointers = calculatePartialParsingPointers(model, 1, 1);
checkParseRegionPointers(parsingPointers, "(a+b+c)", "Parens", "Parens", "Op", "Op", "values");
parsingPointers = calculatePartialParsingPointers(model, 3, 1);
checkParseRegionPointers(parsingPointers, "b", "Multiplication", "Multiplication", "Atom", "Op", "values");
parsingPointers = calculatePartialParsingPointers(model, 5, 2);
checkParseRegionPointers(parsingPointers, model, "Sequence", "Sequence", "Op", null, null);
checkParseRegionPointers(parsingPointers, "(a+b+c)", "Parens", "Parens", "Op", "Op", "values");
parsingPointers = calculatePartialParsingPointers(model, 6, 1);
checkParseRegionPointers(parsingPointers, model, "Sequence", "Sequence", "Op", null, null);
checkParseRegionPointers(parsingPointers, "(a+b+c)", "Parens", "Parens", "Op", "Op", "values");
parsingPointers = calculatePartialParsingPointers(model, 8, 2);
checkParseRegionPointers(parsingPointers, "(c/d)", "Term", "Term", "Op", "Op", "values");
parsingPointers = calculatePartialParsingPointers(model, 9, 2);
checkParseRegionPointers(parsingPointers, "c/d", "Addition", "Addition", "Op", "Op", "values");
checkParseRegionPointers(parsingPointers, "(c/d)", "Parens", "Parens", "Op", "Op", "values");
model="a b";
model = "a b";
parsingPointers = calculatePartialParsingPointers(model, 1, 1);
checkParseRegionPointers(parsingPointers, "a b", "ActionImpl", "Sequence", "Sequence", null, null);
}
@ -56,17 +59,47 @@ public class PartialParsingPointerTest extends AbstractPartialParserTest {
with(LookaheadLanguageStandaloneSetup.class);
String model = "bar a foo bar c b d foo bar b c";
for (int i = 0; i < model.length(); ++i) {
System.out.println(i);
PartialParsingPointers parsingPointers = calculatePartialParsingPointers(model, i, 1);
if (i < 29) {
if (i < 3) {
checkParseRegionPointers(parsingPointers, model, "Entry", "Entry", "Entry", null, null);
}
else if (i < 5) {
checkParseRegionPointers(parsingPointers, "bar a", "LookAhead0", "LookAhead0", "LookAhead0", "Entry",
"contents");
}
else if (i < 9) {
checkParseRegionPointers(parsingPointers, model, "Entry", "Entry", "Entry", null, null);
}
else if (i < 15) {
checkParseRegionPointers(parsingPointers, " foo bar c b d", "Alts", "Alts", "LookAhead1", "Entry",
"contents");
}
else if (i < 19) {
checkParseRegionPointers(parsingPointers, " foo bar c b d", "LookAhead1", "LookAhead1", "LookAhead1",
"Entry", "contents");
}
else if (i < 23) {
checkParseRegionPointers(parsingPointers, model, "Entry", "Entry", "Entry", null, null);
}
else if (i < 29) {
checkParseRegionPointers(parsingPointers, " foo bar b c", "Alts", "Alts", "LookAhead3", "Entry",
"contents");
}
else {
checkParseRegionPointers(parsingPointers, " c", "LookAhead4", "LookAhead4", "LookAhead4", "LookAhead3",
"z");
checkParseRegionPointers(parsingPointers, " foo bar b c", "LookAhead3", "LookAhead3", "LookAhead3", "Entry",
"contents");
}
}
}
public void testReference() throws Exception {
with(ReferenceGrammarStandaloneSetup.class);
String model = "spielplatz 17 { kind ( Dennis 6 ) kind ( Sven 7 ) }";
PartialParsingPointers parsingPointers = calculatePartialParsingPointers(model, model.indexOf("Sven"), 4);
checkParseRegionPointers(parsingPointers, " kind ( Sven 7 )", "Kind", "Kind", "Kind", "Spielplatz", "kinder");
}
private PartialParsingPointers calculatePartialParsingPointers(String model, int changeRegionStart,
int changeRegionSize) throws Exception {
CompositeNode rootNode = getRootNode(model);
@ -113,11 +146,14 @@ public class PartialParsingPointerTest extends AbstractPartialParserTest {
assertEquals(expectedAstParentElementClassName, astParentElementClassName);
String containmentFeatureName = parsingPointers.findASTContainmentFeatureName();
assertEquals(expectedAstParentFeatureName, containmentFeatureName);
if(astParentElement != null) {
EStructuralFeature containmentFeature = astParentElement.eClass().getEStructuralFeature(containmentFeatureName);
if(containmentFeature.isMany()) {
assertTrue(((List<EObject>) astParentElement.eGet(containmentFeature)).contains(parsingPointers.findASTReplaceElement()));
} else {
if (astParentElement != null) {
EStructuralFeature containmentFeature = astParentElement.eClass().getEStructuralFeature(
containmentFeatureName);
if (containmentFeature.isMany()) {
assertTrue(((List<EObject>) astParentElement.eGet(containmentFeature)).contains(parsingPointers
.findASTReplaceElement()));
}
else {
assertTrue(astParentElement.eGet(containmentFeature).equals(parsingPointers.findASTReplaceElement()));
}
}

View file

@ -21,23 +21,23 @@ public class LengthOffsetLineTest extends AbstractGeneratorTest {
CompositeNode node = (CompositeNode) getRootNode(model);
EList<LeafNode> leafNodes = node.getLeafNodes();
Iterator<LeafNode> iter = leafNodes.iterator();
assertEquals(0,iter.next().offset());
assertEquals(7,iter.next().offset());
assertEquals(8,iter.next().offset());
assertEquals(11,iter.next().offset());
assertEquals(12,iter.next().offset());
assertEquals(13,iter.next().offset());
assertEquals(20,iter.next().offset());
assertEquals(21,iter.next().offset());
assertEquals(24,iter.next().offset());
assertEquals(0,iter.next().getOffset());
assertEquals(7,iter.next().getOffset());
assertEquals(8,iter.next().getOffset());
assertEquals(11,iter.next().getOffset());
assertEquals(12,iter.next().getOffset());
assertEquals(13,iter.next().getOffset());
assertEquals(20,iter.next().getOffset());
assertEquals(21,iter.next().getOffset());
assertEquals(24,iter.next().getOffset());
}
public void testOffset2() throws Exception {
String model = "element foo;\nelement bar;";
CompositeNode node = (CompositeNode) getRootNode(model);
Iterator<AbstractNode> iter = node.getChildren().iterator();
assertEquals(0,iter.next().offset());
assertEquals(12,iter.next().offset());
assertEquals(0,iter.next().getOffset());
assertEquals(12,iter.next().getOffset());
assertFalse(iter.hasNext());
}
@ -46,15 +46,15 @@ public class LengthOffsetLineTest extends AbstractGeneratorTest {
CompositeNode node = (CompositeNode) getRootNode(model);
EList<LeafNode> leafNodes = node.getLeafNodes();
Iterator<LeafNode> iter = leafNodes.iterator();
assertEquals(1,iter.next().line());
assertEquals(1,iter.next().line());
assertEquals(1,iter.next().line());
assertEquals(1,iter.next().line());
assertEquals(1,iter.next().line());
assertEquals(2,iter.next().line());
assertEquals(2,iter.next().line());
assertEquals(2,iter.next().line());
assertEquals(2,iter.next().line());
assertEquals(1,iter.next().getLine());
assertEquals(1,iter.next().getLine());
assertEquals(1,iter.next().getLine());
assertEquals(1,iter.next().getLine());
assertEquals(1,iter.next().getLine());
assertEquals(2,iter.next().getLine());
assertEquals(2,iter.next().getLine());
assertEquals(2,iter.next().getLine());
assertEquals(2,iter.next().getLine());
assertFalse(iter.hasNext());
}
@ -62,12 +62,12 @@ public class LengthOffsetLineTest extends AbstractGeneratorTest {
String model = "element foo;\nelement bar;\nelement bar;\nelement bar;";
CompositeNode node = (CompositeNode) getRootNode(model);
Iterator<AbstractNode> iter = node.getChildren().iterator();
assertEquals(1,iter.next().line());
assertEquals(1,iter.next().getLine());
//Note: because preceding whitespace is added to the following node,
// the '\n' is always added to the following composite node
assertEquals(1,iter.next().line());
assertEquals(2,iter.next().line());
assertEquals(3,iter.next().line());
assertEquals(1,iter.next().getLine());
assertEquals(2,iter.next().getLine());
assertEquals(3,iter.next().getLine());
assertFalse(iter.hasNext());
}
@ -80,7 +80,7 @@ public class LengthOffsetLineTest extends AbstractGeneratorTest {
assertEquals(5,nodes.size());
int offset = 0;
for (LeafNode leafNode : nodes) {
assertEquals(offset,leafNode.offset());
assertEquals(offset,leafNode.getOffset());
offset += leafNode.length();
}
}

View file

@ -0,0 +1,25 @@
/*******************************************************************************
* Copyright (c) 2008 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.parsetree;
import org.eclipse.xtext.testlanguages.ReferenceGrammarStandaloneSetup;
import org.eclipse.xtext.tests.AbstractGeneratorTest;
/**
* @author Jan Köhnlein - Initial contribution and API
*
*/
public class NodeContentAdapterTest extends AbstractGeneratorTest{
public void testNodeContentAdapter() throws Exception {
with(ReferenceGrammarStandaloneSetup.class);
CompositeNode rootNode = getRootNode("spielplatz 112 'Jajaja' { kind ( Dennis 7) }");
NodeUtil.dumpCompositeNodes("", rootNode);
}
}

View file

@ -9,8 +9,6 @@
package org.eclipse.xtext.parsetree.reconstr;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.xtext.RuleCall;
import org.eclipse.xtext.parsetree.CompositeNode;
import org.eclipse.xtext.parsetree.IParseTreeConstructor;
import org.eclipse.xtext.parsetree.NodeUtil;
import org.eclipse.xtext.tests.AbstractGeneratorTest;

View file

@ -30,9 +30,9 @@ public class LeafNodeBug_234132 extends AbstractGeneratorTest {
System.out.println("Model length=" + model.length());
for (LeafNode leafNode : leafNodes) {
String text = leafNode.getText();
System.out.println("Leaf node" + leafNode.toString() + " offset=" + leafNode.offset() + " length=" + leafNode.length() + " text=" + ((text != null)? text : ""));
assertTrue(leafNode.length() + leafNode.offset() <= model.length());
assertEquals(model.substring(leafNode.offset(), leafNode.offset() + leafNode.length()), leafNode.getText());
System.out.println("Leaf node" + leafNode.toString() + " offset=" + leafNode.getOffset() + " length=" + leafNode.length() + " text=" + ((text != null)? text : ""));
assertTrue(leafNode.length() + leafNode.getOffset() <= model.length());
assertEquals(model.substring(leafNode.getOffset(), leafNode.getOffset() + leafNode.length()), leafNode.getText());
}
}
}