From c9fc5d9a4bb1d75740eee4cf96f5c40f9885f2bc Mon Sep 17 00:00:00 2001 From: sefftinge Date: Fri, 15 Aug 2008 10:02:37 +0000 Subject: [PATCH] refactored grammar and xtext metamodel --- .../src/org/eclipse/xtext/util/Files.java | 18 ++- .../src/org/eclipse/xtext/GrammarUtil.java | 58 +++---- .../src/org/eclipse/xtext/Xtext.xtext | 8 +- .../src/org/eclipse/xtext/Xtext2.chk | 9 +- .../src/org/eclipse/xtext/XtextUtil.ext | 2 +- .../eclipse/xtext/builtin/XtextBuiltin.xmi | 16 +- .../eclipse/xtext/builtin/XtextBuiltin.xtext | 15 +- .../parser/GenericEcoreElementFactory.java | 13 +- .../parser/antlr/AbstractAntlrParser.java | 54 +++---- .../xtext/parsetree/ParseTreeUtil.java | 144 +++++++++--------- .../eclipse/xtext/resource/XtextResource.java | 32 ++-- .../org/eclipse/xtext/XtextGrammarTest.java | 4 +- .../org/eclipse/xtext/XtextGrammarTest.xtext | 28 ++-- .../AbstractTestLanguage.xtext | 6 +- .../ParseErrorHandlingTest.java | 26 ++-- .../xtext/testlanguages/LexerLanguage.xtext | 3 +- .../xtext/xtext2ecore/BootstrapModelTest.java | 2 +- .../xtext/xtext2ecore/XtextUtilTrafoTest.java | 2 +- 18 files changed, 202 insertions(+), 238 deletions(-) diff --git a/plugins/org.eclipse.xtext.util/src/org/eclipse/xtext/util/Files.java b/plugins/org.eclipse.xtext.util/src/org/eclipse/xtext/util/Files.java index 11b2da7a0..23ec79c58 100644 --- a/plugins/org.eclipse.xtext.util/src/org/eclipse/xtext/util/Files.java +++ b/plugins/org.eclipse.xtext.util/src/org/eclipse/xtext/util/Files.java @@ -33,10 +33,10 @@ public class Files { for (String file : files) { File copy = new File(target.getAbsolutePath() + File.separatorChar + file); if (!copy.exists()) { + String uri = sourceDir + "/" + file; + ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader(); + InputStream is = contextClassLoader.getResourceAsStream(uri); try { - ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader(); - String uri = sourceDir + "/" + file; - InputStream is = contextClassLoader.getResourceAsStream(uri); copy.createNewFile(); FileOutputStream fwr = new FileOutputStream(copy); byte[] buff = new byte[1024]; @@ -45,9 +45,14 @@ public class Files { fwr.write(buff, 0, read); } log.debug("Copied " + copy); - } - catch (IOException e) { + } catch (IOException e) { log.error(e); + } finally { + try { + is.close(); + } catch (IOException e) { + log.error(e); + } } } } @@ -63,8 +68,7 @@ public class Files { final File file = contents[j]; if (file.isDirectory()) { cleanFolder(file); - } - else { + } else { if (!file.delete()) { log.error("Couldn't delete " + file.getAbsolutePath()); } diff --git a/plugins/org.eclipse.xtext/src/org/eclipse/xtext/GrammarUtil.java b/plugins/org.eclipse.xtext/src/org/eclipse/xtext/GrammarUtil.java index d9239ee02..ed26cf1be 100644 --- a/plugins/org.eclipse.xtext/src/org/eclipse/xtext/GrammarUtil.java +++ b/plugins/org.eclipse.xtext/src/org/eclipse/xtext/GrammarUtil.java @@ -152,7 +152,7 @@ public class GrammarUtil { String id = getSuperGrammarId(_this); if (id == null) return null; - if (!(_this.eResource() != null && _this.eResource().getResourceSet() instanceof XtextResourceSet)) + if (!(_this.eResource() != null && _this.eResource().getResourceSet() !=null)) throw new IllegalArgumentException("The passed grammar is not contained in a Resourceset"); ResourceSet resourceSet = _this.eResource().getResourceSet(); URI uri = getClasspathURIForLanguageId(id); @@ -191,51 +191,31 @@ public class GrammarUtil { public static List allRules(Grammar _this) { List result = new ArrayList(); - result.addAll(allParserRules(_this)); - result.addAll(allLexerRules(_this)); + Set names = new HashSet(); + for (AbstractRule rule : _this.getRules()) { + if (names.add(rule.getName())) { + result.add(rule); + } + } + + Grammar superGrammar = getSuperGrammar(_this); + if (superGrammar != null) { + List superParserRules = allRules(superGrammar); + for (AbstractRule r : superParserRules) { + if (names.add(r.getName())) { + result.add(r); + } + } + } return result; } public static List allParserRules(Grammar _this) { - List result = new ArrayList(); - Set names = new HashSet(); - for (ParserRule rule : _this.getParserRules()) { - if (names.add(rule.getName())) { - result.add(rule); - } - } - - Grammar superGrammar = getSuperGrammar(_this); - if (superGrammar != null) { - List superParserRules = allParserRules(superGrammar); - for (ParserRule r : superParserRules) { - if (names.add(r.getName())) { - result.add(r); - } - } - } - return result; + return EcoreUtil2.typeSelect(allRules(_this), ParserRule.class); } public static List allLexerRules(Grammar _this) { - List result = new ArrayList(); - Set names = new HashSet(); - for (LexerRule rule : _this.getLexerRules()) { - if (names.add(rule.getName())) { - result.add(rule); - } - } - - Grammar superGrammar = getSuperGrammar(_this); - if (superGrammar != null) { - List superParserRules = allLexerRules(superGrammar); - for (LexerRule r : superParserRules) { - if (names.add(r.getName())) { - result.add(r); - } - } - } - return result; + return EcoreUtil2.typeSelect(allRules(_this), LexerRule.class); } public static List allMetamodelDeclarations(Grammar _this) { diff --git a/plugins/org.eclipse.xtext/src/org/eclipse/xtext/Xtext.xtext b/plugins/org.eclipse.xtext/src/org/eclipse/xtext/Xtext.xtext index 079217488..cd6ebe54c 100644 --- a/plugins/org.eclipse.xtext/src/org/eclipse/xtext/Xtext.xtext +++ b/plugins/org.eclipse.xtext/src/org/eclipse/xtext/Xtext.xtext @@ -9,12 +9,10 @@ language org.eclipse.xtext.Xtext generate xtext "http://www.eclipse.org/2008/Xtext" -Grammar : +Grammar : (abstract?='abstract language' | 'language') idElements+=ID ('.' idElements+=ID)* ('extends' superGrammarIdElements+=ID ('.' superGrammarIdElements+=ID)*)? metamodelDeclarations+=AbstractMetamodelDeclaration* - parserRules+=ParserRule* - ('lexing' ':' - lexerRules+=LexerRule+)? + (rules+=AbstractRule)+ ; AbstractRule : LexerRule | ParserRule; @@ -29,7 +27,7 @@ ReferencedMetamodel : 'import' uri=STRING ('as' alias=ID)?; LexerRule : - name=ID ('returns' type=TypeRef)? ':' + ('native'|'lexer') name=ID ('returns' type=TypeRef)? ':' body=STRING ';' ; diff --git a/plugins/org.eclipse.xtext/src/org/eclipse/xtext/Xtext2.chk b/plugins/org.eclipse.xtext/src/org/eclipse/xtext/Xtext2.chk index d23f6e670..20d2c01fa 100644 --- a/plugins/org.eclipse.xtext/src/org/eclipse/xtext/Xtext2.chk +++ b/plugins/org.eclipse.xtext/src/org/eclipse/xtext/Xtext2.chk @@ -14,6 +14,7 @@ import xtext; import xtextutil; extension org::eclipse::xtext::Extensions; +extension org::eclipse::xtext::GrammarUtil; /* context Action ERROR "Created class " + typeName.name + " cannot be resolved" : @@ -24,8 +25,8 @@ context Action if resolveCreatedType() != null ERROR "Created class does not inh || ((EClass) containingParserRule().resolveReturnType().eClassifier).isSuperTypeOf((EClass) resolveCreatedType().eClassifier); */ -context Assignment ERROR "Assigned token must be a keyword or a rule call" : - Keyword.isInstance(terminal) || RuleCall.isInstance(terminal); +context RuleCall ERROR "Couldn't resolve called rule "+name : + calledRule()!=null; context GeneratedMetamodel ERROR "Only one generated metamodel can have default alias" : alias != null @@ -38,8 +39,8 @@ context GeneratedMetamodel ERROR "Duplicate aliases are only allowed for referen context Group ERROR "Group cannot contain more than one action" : eAllContents.typeSelect(Action).size <= 1; -context ParserRule ERROR "Name mut be unique" : - grammar().parserRules.select(p | p.name == name).size==1; +context AbstractRule ERROR "Name must be unique" : + grammar().rules.select(p | p.name == name).size==1; /* context ParserRule ERROR "Returned class " + getReturnTypeName() + " cannot be resolved" : diff --git a/plugins/org.eclipse.xtext/src/org/eclipse/xtext/XtextUtil.ext b/plugins/org.eclipse.xtext/src/org/eclipse/xtext/XtextUtil.ext index 37ddd2b68..f437e612e 100644 --- a/plugins/org.eclipse.xtext/src/org/eclipse/xtext/XtextUtil.ext +++ b/plugins/org.eclipse.xtext/src/org/eclipse/xtext/XtextUtil.ext @@ -68,7 +68,7 @@ private setAbstractFeature(Grammar this, List[MetaModel] x) : private cached Boolean isAbstract(ComplexType name, Grammar g) : g.eAllContents.typeSelect(Action).select(e|e.typeName.getQualifiedName() == name.getQualifiedName()).isEmpty && - g.eAllContents.typeSelect(Assignment).select(e|(e.currentType(g).getQualifiedName()) == (name.getQualifiedName())).isEmpty + g.eAllContents.typeSelect(Assignment).select(e|(if e.currentType(g)==null then info(e)) -> (e.currentType(g).getQualifiedName()) == (name.getQualifiedName())).isEmpty ; diff --git a/plugins/org.eclipse.xtext/src/org/eclipse/xtext/builtin/XtextBuiltin.xmi b/plugins/org.eclipse.xtext/src/org/eclipse/xtext/builtin/XtextBuiltin.xmi index 728f65c3d..a67b9ccf2 100644 --- a/plugins/org.eclipse.xtext/src/org/eclipse/xtext/builtin/XtextBuiltin.xmi +++ b/plugins/org.eclipse.xtext/src/org/eclipse/xtext/builtin/XtextBuiltin.xmi @@ -7,14 +7,14 @@ builtin XtextBuiltin - - + + - - - - - - + + + + diff --git a/plugins/org.eclipse.xtext/src/org/eclipse/xtext/builtin/XtextBuiltin.xtext b/plugins/org.eclipse.xtext/src/org/eclipse/xtext/builtin/XtextBuiltin.xtext index 9461db5fe..543c01c96 100644 --- a/plugins/org.eclipse.xtext/src/org/eclipse/xtext/builtin/XtextBuiltin.xtext +++ b/plugins/org.eclipse.xtext/src/org/eclipse/xtext/builtin/XtextBuiltin.xtext @@ -3,18 +3,17 @@ abstract language org.eclipse.xtext.builtin.XtextBuiltIn_Temp import "http://www.eclipse.org/emf/2002/Ecore" as ecore; -lexing: -ID : "('^')?('a'..'z'|'A'..'Z'|'_') ('a'..'z'|'A'..'Z'|'_'|'0'..'9')*"; -INT returns ecore::EInt +lexer ID : "('^')?('a'..'z'|'A'..'Z'|'_') ('a'..'z'|'A'..'Z'|'_'|'0'..'9')*"; +lexer INT returns ecore::EInt : "('0'..'9')+"; -STRING : " +lexer STRING : " '\"' ( '\\\\' ('b'|'t'|'n'|'f'|'r'|'\\\"'|'\\''|'\\\\') | ~('\\\\'|'\"') )* '\"' | '\\'' ( '\\\\' ('b'|'t'|'n'|'f'|'r'|'\\\"'|'\\''|'\\\\') | ~('\\\\'|'\\'') )* '\\'' "; -ML_COMMENT : "'/*' ( options {greedy=false;} : . )* '*/' {$channel=HIDDEN;}"; -SL_COMMENT : "'//' ~('\\n'|'\\r')* '\\r'? '\\n' {$channel=HIDDEN;}"; +lexer ML_COMMENT : "'/*' ( options {greedy=false;} : . )* '*/' {$channel=HIDDEN;}"; +lexer SL_COMMENT : "'//' ~('\\n'|'\\r')* '\\r'? '\\n' {$channel=HIDDEN;}"; -WS : "(' '|'\\t'|'\\r'|'\\n')+ {$channel=HIDDEN;}"; +lexer WS : "(' '|'\\t'|'\\r'|'\\n')+ {$channel=HIDDEN;}"; -ANY_OTHER : "."; +lexer ANY_OTHER : "."; diff --git a/plugins/org.eclipse.xtext/src/org/eclipse/xtext/parser/GenericEcoreElementFactory.java b/plugins/org.eclipse.xtext/src/org/eclipse/xtext/parser/GenericEcoreElementFactory.java index d7cb39b63..e0f8f8c9c 100755 --- a/plugins/org.eclipse.xtext/src/org/eclipse/xtext/parser/GenericEcoreElementFactory.java +++ b/plugins/org.eclipse.xtext/src/org/eclipse/xtext/parser/GenericEcoreElementFactory.java @@ -46,9 +46,12 @@ public class GenericEcoreElementFactory implements IAstFactory { public EObject create(String fullTypeName) { EClass clazz = getEClass(fullTypeName); - if (clazz != null && !(clazz.isAbstract() || clazz.isInterface())) - return clazz.getEPackage().getEFactoryInstance().create(clazz); - return null; + if (clazz == null) + throw new IllegalArgumentException("Coudln't find EClass for name " + fullTypeName); + + if (clazz.isAbstract() || clazz.isInterface()) + throw new IllegalArgumentException("Can't create instance of abstract type " + fullTypeName); + return clazz.getEPackage().getEFactoryInstance().create(clazz); } @Deprecated @@ -103,10 +106,10 @@ public class GenericEcoreElementFactory implements IAstFactory { if (decl.getAlias() == null && alias == null || decl.getAlias() != null && decl.getAlias().equals(alias)) { if (decl instanceof GeneratedMetamodel) { GeneratedMetamodel mm = (GeneratedMetamodel) decl; - return EcoreUtil2.loadEPackage(mm.getNsURI(),grammarAccess.getClass().getClassLoader()); + return EcoreUtil2.loadEPackage(mm.getNsURI(), grammarAccess.getClass().getClassLoader()); } else { ReferencedMetamodel mm = (ReferencedMetamodel) decl; - return EcoreUtil2.loadEPackage(mm.getUri(),grammarAccess.getClass().getClassLoader()); + return EcoreUtil2.loadEPackage(mm.getUri(), grammarAccess.getClass().getClassLoader()); } } } diff --git a/plugins/org.eclipse.xtext/src/org/eclipse/xtext/parser/antlr/AbstractAntlrParser.java b/plugins/org.eclipse.xtext/src/org/eclipse/xtext/parser/antlr/AbstractAntlrParser.java index 5ae5b4664..e061a19e7 100644 --- a/plugins/org.eclipse.xtext/src/org/eclipse/xtext/parser/antlr/AbstractAntlrParser.java +++ b/plugins/org.eclipse.xtext/src/org/eclipse/xtext/parser/antlr/AbstractAntlrParser.java @@ -70,13 +70,16 @@ public abstract class AbstractAntlrParser extends Parser { } protected void associateNodeWithAstElement(CompositeNode node, EObject astElement) { + if (astElement == null) + throw new NullPointerException("passed astElement was null"); + if (node == null) + throw new NullPointerException("passed node was null"); if (node.getElement() != null && node.getElement() != astElement) { throw new ParseException("Reassignment of astElement in parse tree node"); } - node.setElement(astElement); - if (astElement instanceof EObject) { - EObject eObject = (EObject) astElement; - NodeAdapter adapter = (NodeAdapter) NodeAdapterFactory.INSTANCE.adapt(eObject, AbstractNode.class); + if (node.getElement() != astElement) { + node.setElement(astElement); + NodeAdapter adapter = (NodeAdapter) NodeAdapterFactory.INSTANCE.adapt(astElement, AbstractNode.class); adapter.setParserNode(node); } } @@ -103,7 +106,8 @@ public abstract class AbstractAntlrParser extends Parser { } private EObject getGrammarElement(String grammarElementID) { - URI resolved = new ClassloaderClasspathUriResolver().resolve(getClass().getClassLoader(),URI.createURI(grammarElementID)); + URI resolved = new ClassloaderClasspathUriResolver().resolve(getClass().getClassLoader(), URI + .createURI(grammarElementID)); return grammar.eResource().getResourceSet().getEObject(resolved, true); } @@ -111,9 +115,10 @@ public abstract class AbstractAntlrParser extends Parser { public Map getTokenTypeMap() { if (antlrTypeToLexerName == null) { + InputStream tokenFile = getTokenFile(); try { + BufferedReader br = new BufferedReader(new InputStreamReader(tokenFile)); antlrTypeToLexerName = new HashMap(); - BufferedReader br = new BufferedReader(new InputStreamReader(getTokenFile())); String line = br.readLine(); Pattern pattern = Pattern.compile("(.*)=(\\d+)"); while (line != null) { @@ -129,10 +134,15 @@ 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); + } finally { + try { + tokenFile.close(); + } catch (IOException e) { + throw new WrappedException(e); + } } } return antlrTypeToLexerName; @@ -190,8 +200,7 @@ public abstract class AbstractAntlrParser extends Parser { EList leafNodes = currentNode.getLeafNodes(); if (leafNodes.isEmpty()) { appendError(currentNode); - } - else { + } else { appendError(leafNodes.get(leafNodes.size() - 1)); } } @@ -266,8 +275,7 @@ 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; @@ -276,17 +284,14 @@ 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(); - } - finally { + } finally { try { appendAllTokens(); - } - finally { + } finally { result = new ParseResult(current, currentNode); } } @@ -298,12 +303,10 @@ 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; @@ -331,8 +334,7 @@ public abstract class AbstractAntlrParser extends Parser { LeafNode leafNode = token2NodeMap.get(lookaheadToken); if (leafNode == null) { deferredLookaheadMap.put(lookaheadToken, currentNode); - } - else { + } else { currentNode.getLookaheadLeafNodes().add(leafNode); } } @@ -379,9 +381,7 @@ public abstract class AbstractAntlrParser extends Parser { } } - protected InputStream getTokenFile() { - return null; - } + protected abstract InputStream getTokenFile(); /** * @return diff --git a/plugins/org.eclipse.xtext/src/org/eclipse/xtext/parsetree/ParseTreeUtil.java b/plugins/org.eclipse.xtext/src/org/eclipse/xtext/parsetree/ParseTreeUtil.java index 829f00025..9db057122 100644 --- a/plugins/org.eclipse.xtext/src/org/eclipse/xtext/parsetree/ParseTreeUtil.java +++ b/plugins/org.eclipse.xtext/src/org/eclipse/xtext/parsetree/ParseTreeUtil.java @@ -8,24 +8,16 @@ *******************************************************************************/ package org.eclipse.xtext.parsetree; -import java.util.ArrayList; import java.util.LinkedHashSet; -import java.util.List; import java.util.Set; import org.eclipse.core.runtime.Assert; import org.eclipse.emf.ecore.EObject; import org.eclipse.emf.ecore.util.EcoreUtil; import org.eclipse.xtext.AbstractElement; -import org.eclipse.xtext.Alternatives; -import org.eclipse.xtext.Assignment; import org.eclipse.xtext.Grammar; -import org.eclipse.xtext.Group; import org.eclipse.xtext.ParserRule; import org.eclipse.xtext.RuleCall; -import org.eclipse.xtext.parsetree.AbstractNode; -import org.eclipse.xtext.parsetree.CompositeNode; -import org.eclipse.xtext.parsetree.LeafNode; /** * @@ -162,32 +154,32 @@ public final class ParseTreeUtil { } } - - /** - * - * Returns a list of all assignment to the given rule. - * - * @param parserRule - * the rule of the assignments (ruleCall) to match - * @return a list containing all {@see org.eclipse.xtext.Assignment} to the - * given rule. - */ - public static final List getParserRuleAssignments(ParserRule parserRule) { - assertParameterNotNull(parserRule, "parserRule"); - List list = new ArrayList(); - Grammar grammar = (Grammar) parserRule.eContainer(); - // filter and search - for (ParserRule rule : grammar.getParserRules()) { - // excluded? - if (!parserRule.equals(rule)) { - Assignment ruleAssignment = getParserRuleAssignment(rule.getAlternatives(), parserRule); - if (ruleAssignment != null) { - list.add(ruleAssignment); - } - } - } - return list; - } +// +// /** +// * +// * Returns a list of all assignment to the given rule. +// * +// * @param parserRule +// * the rule of the assignments (ruleCall) to match +// * @return a list containing all {@see org.eclipse.xtext.Assignment} to the +// * given rule. +// */ +// public static final List getParserRuleAssignments(ParserRule parserRule) { +// assertParameterNotNull(parserRule, "parserRule"); +// List list = new ArrayList(); +// Grammar grammar = (Grammar) parserRule.eContainer(); +// // filter and search +// for (ParserRule rule : GrammarUtil.allParserRules(grammar)) { +// // excluded? +// if (!parserRule.equals(rule)) { +// Assignment ruleAssignment = getParserRuleAssignment(rule.getAlternatives(), parserRule); +// if (ruleAssignment != null) { +// list.add(ruleAssignment); +// } +// } +// } +// return list; +// } /** * asserts if the given parameter object isnt null @@ -225,48 +217,48 @@ public final class ParseTreeUtil { return abstractElement; } - /** - * - * @param contextElement - * element searched for assignments to the given rule - * @param parserRule - * the rule of the assignments to search for - * @return an assignment object containing a rulecall to the given - * parserRule or null if not found. - */ - private static final Assignment getParserRuleAssignment(AbstractElement contextElement, ParserRule parserRule) { - - assertParameterNotNull(contextElement, "contextElement"); - assertParameterNotNull(parserRule, "parserRule"); - - Assignment assignment = null; - if (contextElement instanceof Group) { - Group group = (Group) contextElement; - for (AbstractElement groupElement : group.getAbstractTokens()) { - assignment = getParserRuleAssignment(groupElement, parserRule); - if (null != assignment) { - break; - } - } - } - else if (contextElement instanceof Alternatives) { - Alternatives alternatives = (Alternatives) contextElement; - for (AbstractElement groupElement : alternatives.getGroups()) { - assignment = getParserRuleAssignment(groupElement, parserRule); - if (null != assignment) { - break; - } - } - } - else if (contextElement instanceof Assignment) { - Assignment assignmentToMatch = (Assignment) contextElement; - if (assignmentToMatch.getTerminal() instanceof RuleCall - && ((RuleCall) assignmentToMatch.getTerminal()).getName().equalsIgnoreCase(parserRule.getName())) { - assignment = assignmentToMatch; - } - } - return assignment; - } +// /** +// * +// * @param contextElement +// * element searched for assignments to the given rule +// * @param parserRule +// * the rule of the assignments to search for +// * @return an assignment object containing a rulecall to the given +// * parserRule or null if not found. +// */ +// private static final Assignment getParserRuleAssignment(AbstractElement contextElement, ParserRule parserRule) { +// +// assertParameterNotNull(contextElement, "contextElement"); +// assertParameterNotNull(parserRule, "parserRule"); +// +// Assignment assignment = null; +// if (contextElement instanceof Group) { +// Group group = (Group) contextElement; +// for (AbstractElement groupElement : group.getAbstractTokens()) { +// assignment = getParserRuleAssignment(groupElement, parserRule); +// if (null != assignment) { +// break; +// } +// } +// } +// else if (contextElement instanceof Alternatives) { +// Alternatives alternatives = (Alternatives) contextElement; +// for (AbstractElement groupElement : alternatives.getGroups()) { +// assignment = getParserRuleAssignment(groupElement, parserRule); +// if (null != assignment) { +// break; +// } +// } +// } +// else if (contextElement instanceof Assignment) { +// Assignment assignmentToMatch = (Assignment) contextElement; +// if (assignmentToMatch.getTerminal() instanceof RuleCall +// && ((RuleCall) assignmentToMatch.getTerminal()).getName().equalsIgnoreCase(parserRule.getName())) { +// assignment = assignmentToMatch; +// } +// } +// return assignment; +// } /** * @param abstractNode diff --git a/plugins/org.eclipse.xtext/src/org/eclipse/xtext/resource/XtextResource.java b/plugins/org.eclipse.xtext/src/org/eclipse/xtext/resource/XtextResource.java index 1a8a3fca2..a44797f4b 100644 --- a/plugins/org.eclipse.xtext/src/org/eclipse/xtext/resource/XtextResource.java +++ b/plugins/org.eclipse.xtext/src/org/eclipse/xtext/resource/XtextResource.java @@ -18,23 +18,22 @@ import java.util.Map; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; -import org.eclipse.emf.common.notify.Adapter; -import org.eclipse.emf.common.util.EList; import org.eclipse.emf.common.util.TreeIterator; import org.eclipse.emf.common.util.URI; import org.eclipse.emf.ecore.EObject; import org.eclipse.emf.ecore.resource.impl.ResourceImpl; import org.eclipse.emf.ecore.util.EcoreUtil; +import org.eclipse.xtext.conversion.IValueConverterService; import org.eclipse.xtext.crossref.BrokenLink; import org.eclipse.xtext.crossref.IFragmentProvider; import org.eclipse.xtext.crossref.ILinker; import org.eclipse.xtext.parser.IAstFactory; import org.eclipse.xtext.parser.IParseResult; import org.eclipse.xtext.parser.IParser; -import org.eclipse.xtext.parsetree.AbstractNode; import org.eclipse.xtext.parsetree.CompositeNode; -import org.eclipse.xtext.parsetree.NodeAdapter; import org.eclipse.xtext.parsetree.NodeContentAdapter; +import org.eclipse.xtext.parsetree.reconstr.IParseTreeConstructor; +import org.eclipse.xtext.parsetree.reconstr.callbacks.WhitespacePreservingCallback; import org.eclipse.xtext.service.Inject; /** @@ -54,6 +53,12 @@ public class XtextResource extends ResourceImpl { @Inject private IFragmentProvider fragmentProvider; + + @Inject + private IParseTreeConstructor parseTreeConstructor; + + @Inject + private IValueConverterService valueConverterService; private Log log = LogFactory.getLog(getClass()); @@ -146,23 +151,10 @@ public class XtextResource extends ResourceImpl { } if (!contents.isEmpty()) { EObject rootElement = contents.get(0); - NodeAdapter rootNodeAdapter = getNodeAdapter(rootElement); - if (rootNodeAdapter != null) { - CompositeNode rootNode = rootNodeAdapter.getParserNode(); - String serialize = rootNode.serialize(); - outputStream.write(serialize.getBytes()); - } + WhitespacePreservingCallback cb = new WhitespacePreservingCallback(valueConverterService); + parseTreeConstructor.update(rootElement, cb); + outputStream.write(cb.toString().getBytes()); } } - private NodeAdapter getNodeAdapter(EObject object) { - EList adapters = object.eAdapters(); - for (Adapter adapter : adapters) { - if (adapter.isAdapterForType(AbstractNode.class)) { - return (NodeAdapter) adapter; - } - } - return null; - } - } diff --git a/tests/org.eclipse.xtext.generator.tests/src/org/eclipse/xtext/XtextGrammarTest.java b/tests/org.eclipse.xtext.generator.tests/src/org/eclipse/xtext/XtextGrammarTest.java index bb6e92637..8c97d531b 100644 --- a/tests/org.eclipse.xtext.generator.tests/src/org/eclipse/xtext/XtextGrammarTest.java +++ b/tests/org.eclipse.xtext.generator.tests/src/org/eclipse/xtext/XtextGrammarTest.java @@ -26,8 +26,8 @@ public class XtextGrammarTest extends AbstractGeneratorTest { public void testInstantiate() throws Exception { EObject grammar = (EObject) getModel("language foo generate foo 'bar' Foo : name=ID;"); - assertWithXtend("'Foo'","parserRules.first().name",grammar); - assertWithXtend("'name'","parserRules.first().alternatives.feature",grammar); + assertWithXtend("'Foo'","rules.first().name",grammar); + assertWithXtend("'name'","rules.first().alternatives.feature",grammar); } } diff --git a/tests/org.eclipse.xtext.generator.tests/src/org/eclipse/xtext/XtextGrammarTest.xtext b/tests/org.eclipse.xtext.generator.tests/src/org/eclipse/xtext/XtextGrammarTest.xtext index 951a916a3..be3260962 100644 --- a/tests/org.eclipse.xtext.generator.tests/src/org/eclipse/xtext/XtextGrammarTest.xtext +++ b/tests/org.eclipse.xtext.generator.tests/src/org/eclipse/xtext/XtextGrammarTest.xtext @@ -11,11 +11,9 @@ language org.eclipse.xtext.XtextGrammarTest generate XtextTest "http://www.eclipse.org/2008/Test/XtextTest" Grammar : - ((abstract?='abstract language' | 'language') idElements+=ID ('.' idElements+=ID)* ('extends' superGrammarIdElements+=ID ('.' superGrammarIdElements+=ID)*)?)? + (abstract?='abstract language' | 'language') idElements+=ID ('.' idElements+=ID)* ('extends' superGrammarIdElements+=ID ('.' superGrammarIdElements+=ID)*)? metamodelDeclarations+=AbstractMetamodelDeclaration* - parserRules+=ParserRule* - ('lexing' ':' - lexerRules+=LexerRule+)? + (rules+=AbstractRule)+ ; AbstractRule : LexerRule | ParserRule; @@ -29,9 +27,9 @@ GeneratedMetamodel : ReferencedMetamodel : 'import' uri=STRING ('as' alias=ID)?; -LexerRule : - name=ID ('returns' type=TypeRef)? ':' - body=STRING +LexerRule : + ('native'|'lexer') name=ID ('returns' type=TypeRef)? ':' + body=STRING ';' ; ParserRule : @@ -43,6 +41,7 @@ ParserRule : TypeRef : (alias=ID '::')? name=ID ; + Alternatives returns AbstractElement: Group ({Alternatives.groups+=current} '|' groups+=Group)* ; @@ -54,16 +53,20 @@ Group returns AbstractElement: AbstractToken returns AbstractElement: (Assignment | Action | - AbstractTerminal) cardinality=('?'|'*'|'+')?; + AbstractTerminal) (cardinality=('?'|'*'|'+'))?; Assignment returns Assignment: feature=ID operator=('+='|'='|'?=') terminal=AbstractTerminal; Action returns Action: - '{' ('current' '=')? typeName=TypeRef ('.' feature=ID operator=('='|'+=') 'current')? '}'; + '{' ('current' '=')? typeName=TypeRef '.' feature=ID operator=('='|'+=') 'current' '}';//TODO make assignment optional AbstractTerminal returns AbstractElement: - Keyword | RuleCall | ParenthesizedElement + Keyword | RuleCall | ParenthesizedElement | CrossReference +; + +CrossReference : + '[' type=TypeRef ('|' rule=RuleCall)? ']' ; ParenthesizedElement returns AbstractElement: @@ -76,9 +79,4 @@ Keyword : RuleCall : name=ID ; - -// lexing: -// Problem: LEXER_BODY kann nicht in xtxt beschrieben werden, da das erste '#>' bereits matcht -// Lösung: built-in lexer token -// LEXER_BODY : <# '<#' ( options {greedy=false;} : . )* '#>' #> diff --git a/tests/org.eclipse.xtext.generator.tests/src/org/eclipse/xtext/grammarinheritance/AbstractTestLanguage.xtext b/tests/org.eclipse.xtext.generator.tests/src/org/eclipse/xtext/grammarinheritance/AbstractTestLanguage.xtext index 1efb477cc..3e7f6b724 100644 --- a/tests/org.eclipse.xtext.generator.tests/src/org/eclipse/xtext/grammarinheritance/AbstractTestLanguage.xtext +++ b/tests/org.eclipse.xtext.generator.tests/src/org/eclipse/xtext/grammarinheritance/AbstractTestLanguage.xtext @@ -5,8 +5,6 @@ import "classpath:/org/eclipse/xtext/grammarinheritance/ametamodel.ecore" as mm InheritedParserRule returns mm::AType : 'element' name=ID; -lexing: +lexer REAL returns ecore::EDouble : "RULE_INT '.' RULE_INT"; -REAL returns ecore::EDouble : "RULE_INT '.' RULE_INT"; - -ID : "('^')?('a'..'z'|'A'..'Z'|'ö'|'ä'|'ü'|'_') ('a'..'z'|'A'..'Z'|'_'|'0'..'9')*"; \ No newline at end of file +lexer ID : "('^')?('a'..'z'|'A'..'Z'|'ö'|'ä'|'ü'|'_') ('a'..'z'|'A'..'Z'|'_'|'0'..'9')*"; \ No newline at end of file diff --git a/tests/org.eclipse.xtext.generator.tests/src/org/eclipse/xtext/parseerrorhandling/ParseErrorHandlingTest.java b/tests/org.eclipse.xtext.generator.tests/src/org/eclipse/xtext/parseerrorhandling/ParseErrorHandlingTest.java index 6c29ef742..2e42ba408 100644 --- a/tests/org.eclipse.xtext.generator.tests/src/org/eclipse/xtext/parseerrorhandling/ParseErrorHandlingTest.java +++ b/tests/org.eclipse.xtext.generator.tests/src/org/eclipse/xtext/parseerrorhandling/ParseErrorHandlingTest.java @@ -27,47 +27,47 @@ public class ParseErrorHandlingTest extends AbstractGeneratorTest { } public void testLexError() throws Exception { - EObject root = getModel("import 'holla' % as foo"); + EObject root = getModel("language a import 'holla' % as foo"); EList errors = NodeUtil.getRootNode(root).allSyntaxErrors(); assertEquals(1,errors.size()); assertEquals("%", ((LeafNode)errors.get(0).getNode()).getText()); assertEquals(1, errors.get(0).getNode().getLine()); - assertEquals(15, errors.get(0).getNode().getOffset()); + assertEquals(26, errors.get(0).getNode().getOffset()); assertEquals(1, errors.get(0).getNode().getLength()); assertEquals(1, errors.size()); } public void testParseError1() throws Exception { - EObject root = getModel("import 'holla' foo returns x::y::Z : name=ID;"); + EObject root = getModel("language a import 'holla' foo returns x::y::Z : name=ID;"); EList errors = NodeUtil.getRootNode(root).allSyntaxErrors(); assertEquals("::", ((LeafNode)errors.get(0).getNode()).getText()); assertEquals(1, errors.get(0).getNode().getLine()); - assertEquals(31, errors.get(0).getNode().getOffset()); + assertEquals(42, errors.get(0).getNode().getOffset()); assertEquals(2, errors.get(0).getNode().getLength()); assertEquals(1, errors.size()); } public void testParseError2() throws Exception { - Object object = getModel("import 'holla' foo returns y::Z : name=ID #;"); - assertWithXtend("'ID'", "parserRules.first().eAllContents.typeSelect(XtextTest::RuleCall).first().name", object); + Object object = getModel("language a import 'holla' foo returns y::Z : name=ID #;"); + assertWithXtend("'ID'", "rules.first().eAllContents.typeSelect(XtextTest::RuleCall).first().name", object); } public void testParseError3() throws Exception { - Object object = getModel("import 'holla' foo returns y::Z : name=ID #############"); - assertWithXtend("'ID'", "parserRules.first().eAllContents.typeSelect(XtextTest::RuleCall).first().name", object); + Object object = getModel("language a import 'holla' foo returns y::Z : name=ID #############"); + assertWithXtend("'ID'", "rules.first().eAllContents.typeSelect(XtextTest::RuleCall).first().name", object); } public void testParseError4() throws Exception { - Object object = getModel("import 'holla' foo returns y::Z : name=ID # 'foo'; bar : 'stuff'"); + Object object = getModel("language a import 'holla' foo returns y::Z : name=ID # 'foo'; bar : 'stuff'"); //System.out.println(errors); - assertWithXtend("'ID'", "parserRules.first().eAllContents.typeSelect(XtextTest::RuleCall).first().name", object); - assertWithXtend("null", "parserRules.first().eAllContents.typeSelect(XtextTest::Keyword).first().name", object); - assertWithXtend("'stuff'", "parserRules.get(1).eAllContents.typeSelect(XtextTest::Keyword).first().value", object); + assertWithXtend("'ID'", "rules.first().eAllContents.typeSelect(XtextTest::RuleCall).first().name", object); + assertWithXtend("null", "rules.first().eAllContents.typeSelect(XtextTest::Keyword).first().name", object); + assertWithXtend("'stuff'", "rules.get(1).eAllContents.typeSelect(XtextTest::Keyword).first().value", object); } public void testname() throws Exception { - String model = "import 'holla' foo returns y::Z : name=ID # 'foo'; bar : 'stuff'"; + String model = "language a import 'holla' foo returns y::Z : name=ID # 'foo'; bar : 'stuff'"; for (int i=model.length();0 result = (List) facade.call("getAllMetaModels", grammarModel); MetaModel xtext = (MetaModel) invokeWithXtend("select(e|e.alias()==null).first()", result); MetaModel ecore = (MetaModel) invokeWithXtend("select(e|e.alias()=='ecore').first()", result); - assertEquals(15,xtext.getTypes().size()); + assertEquals(16,xtext.getTypes().size()); assertEquals(3,ecore.getTypes().size()); for(AbstractType t : xtext.getTypes()) { diff --git a/tests/org.eclipse.xtext.generator.tests/src/org/eclipse/xtext/xtext2ecore/XtextUtilTrafoTest.java b/tests/org.eclipse.xtext.generator.tests/src/org/eclipse/xtext/xtext2ecore/XtextUtilTrafoTest.java index 2c00407a3..a551b703d 100644 --- a/tests/org.eclipse.xtext.generator.tests/src/org/eclipse/xtext/xtext2ecore/XtextUtilTrafoTest.java +++ b/tests/org.eclipse.xtext.generator.tests/src/org/eclipse/xtext/xtext2ecore/XtextUtilTrafoTest.java @@ -38,7 +38,7 @@ public class XtextUtilTrafoTest extends AbstractGeneratorTest { + "Main : ^import+=Import* types+=Type*;" + "Import : 'import' uri=STRING;" + "Type : 'type' name=ID 'extends' ^extends=[Type];"); - RuleCall rc = (RuleCall) invokeWithXtend("parserRules.first().eAllContents.typeSelect(xtext::RuleCall).first()", model); + RuleCall rc = (RuleCall) invokeWithXtend("rules.first().eAllContents.typeSelect(xtext::RuleCall).first()", model); assertNotNull(rc); assertWithXtend("'Main'", "testCurrentType().name", rc); assertWithXtend("false", "testCurrentType().abstract", rc);