[263773] Xtext grammar enhancements (src)

- Explicit rule calls and super rule calls
  - name=super::ID
  - name=Terminals::ID
  - feature=com::acme::MyLang::SomeRule
- Parser rule fragments
  - fragment DeclarationOwner:
      declarations+=Declaration+;
- Parameterized rule calls:
  - IdOrKeyword<AllowKeyword>:
      <AllowKeyword> ‘keyword’ | ID;

This commit contains the changes to the Xtext core
plugins without any changes to generated code.
This commit is contained in:
Sebastian Zarnekow 2015-08-12 17:11:05 +02:00
parent 1f642242c2
commit 9185cb9cca
61 changed files with 1891 additions and 403 deletions

View file

@ -44,6 +44,7 @@ import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import com.google.common.base.Joiner;
import com.google.inject.Guice;
import com.google.inject.Injector;
import com.google.inject.Key;
@ -261,9 +262,9 @@ public abstract class AbstractXtextTests extends Assert implements ResourceLoadH
checkNodeModel(resource);
if (expectedErrors != UNKNOWN_EXPECTATION) {
if (expectedErrors == EXPECT_ERRORS)
assertFalse(resource.getErrors().toString(), resource.getErrors().isEmpty());
assertFalse(Joiner.on('\n').join(resource.getErrors()), resource.getErrors().isEmpty());
else
assertEquals(resource.getErrors().toString(), expectedErrors, resource.getErrors().size());
assertEquals(Joiner.on('\n').join(resource.getErrors()), expectedErrors, resource.getErrors().size());
}
for(Diagnostic d: resource.getErrors()) {
if (d instanceof ExceptionDiagnostic)

View file

@ -14,6 +14,7 @@ import org.eclipse.emf.common.notify.Adapter;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.emf.ecore.util.EcoreUtil;
import org.eclipse.xtext.IGrammarAccess;
import org.eclipse.xtext.junit4.util.ParseHelper;
import org.eclipse.xtext.junit4.validation.ValidationTestHelper;
import org.eclipse.xtext.nodemodel.ICompositeNode;
@ -66,6 +67,9 @@ public class SerializerTester {
@Inject
protected ValidationTestHelper validationHelper;
@Inject
private IGrammarAccess grammarAccess;
/**
* @since 2.3
@ -174,7 +178,7 @@ public class SerializerTester {
if (Iterables.size(contexts) != 1) {
StringBuilder msg = new StringBuilder();
msg.append("One context is expected, but " + Iterables.size(contexts) + " have been found\n");
msg.append("Contexts: " + Joiner.on(", ").join(Iterables.transform(contexts, new Context2NameFunction())));
msg.append("Contexts: " + Joiner.on(", ").join(Iterables.transform(contexts, new Context2NameFunction().toFunction(grammarAccess.getGrammar()))));
msg.append("Semantic Object: " + EmfFormatter.objPath(semanticObject));
Assert.fail(msg.toString());
}

View file

@ -28,6 +28,7 @@ import org.eclipse.xtext.Grammar
import org.eclipse.xtext.GrammarUtil
import org.eclipse.xtext.Keyword
import org.eclipse.xtext.RuleCall
import org.eclipse.xtext.RuleNames
import org.eclipse.xtext.TypeRef
import org.eclipse.xtext.XtextRuntimeModule
import org.eclipse.xtext.formatting.ILineSeparatorInformation
@ -137,9 +138,18 @@ class GrammarAccessExtensions {
/**
* Creates an identifier for a Rule which is a valid Java identifier and unique within
* the Rule's grammar.
* the Rule's grammar and its super grammars.
*/
def String gaRuleIdentifier(AbstractRule rule) {
val plainName = RuleNames.getRuleNames(rule).getUniqueRuleName(rule);
return toJavaIdentifier(plainName, true);
}
/**
* Creates an identifier for a Rule which is a valid Java identifier and unique within
* the grammar that defines the rule.
*/
def String gaBaseRuleIdentifier(AbstractRule rule) {
rule.name.toJavaIdentifier(true)
}
@ -223,6 +233,13 @@ class GrammarAccessExtensions {
def String gaRuleAccessMethodName(AbstractRule rule) {
'get' + rule.gaRuleIdentifier + 'Rule'
}
/**
* Returns the method name for accessing a rule via a GrammarAccess implementation.
*/
def String gaBaseRuleAccessMethodName(AbstractRule rule) {
'get' + rule.gaBaseRuleIdentifier + 'Rule'
}
/**
* Returns the method name for accessing a rule's content via a ParserRuleAccess implementation.
@ -231,6 +248,13 @@ class GrammarAccessExtensions {
'get' + rule.gaRuleIdentifier + 'Access'
}
/**
* Returns the method name for accessing a rule's content via a ParserRuleAccess implementation.
*/
def String gaBaseRuleElementsMethodName(AbstractRule rule) {
'get' + rule.gaBaseRuleIdentifier + 'Access'
}
/**
* Returns the method name for accessing an element via a GrammarAccess implementation.
*/
@ -245,6 +269,14 @@ class GrammarAccessExtensions {
def String gaRuleAccessorClassName(AbstractRule rule) {
rule.gaRuleIdentifier + 'Elements'
}
/**
* Returns the simple class name of a rule's facade. A GrammarAccess implementation has
* a facade for each parser rule, which contains the methods for accessing the rule's elements.
*/
def String gaBaseRuleAccessorClassName(AbstractRule rule) {
rule.gaBaseRuleIdentifier + 'Elements'
}
/**
* Returns the invocation of the rule accessor method as Java statement.
@ -253,12 +285,26 @@ class GrammarAccessExtensions {
rule.gaRuleAccessMethodName + '()'
}
/**
* Returns the invocation of the rule accessor method as Java statement.
*/
def String gaBaseRuleAccessor(AbstractRule rule) {
rule.gaBaseRuleAccessMethodName + '()'
}
/**
* Returns the invocation of the rule's content accessor method as Java statement.
*/
def String gaElementsAccessor(AbstractRule rule) {
rule.gaRuleElementsMethodName + '()'
}
/**
* Returns the invocation of the rule's content accessor method as Java statement.
*/
def String gaBaseElementsAccessor(AbstractRule rule) {
rule.gaBaseRuleElementsMethodName + '()'
}
/**
* Returns the invocation of the element accessor method as Java statement.
@ -327,7 +373,7 @@ class GrammarAccessExtensions {
return result;
}
@FinalFieldsConstructor
@FinalFieldsConstructor
protected static class LineSeparatorModule extends XtextRuntimeModule {
val ILineSeparatorInformation lineSeparatorInformation;

View file

@ -77,6 +77,10 @@ class GrammarAccessFragment2 extends AbstractGeneratorFragment2 {
writeGrammar(language)
}
def protected String getQualifiedName(AbstractRule rule) {
return GrammarUtil.getGrammar(rule).name + '.' + rule.name
}
protected def void writeGrammar(LanguageConfig2 language) {
val isSaving = Wrapper.wrap(false)
val ResourceSet cloneInto = new ResourceSetImpl
@ -246,7 +250,7 @@ class GrammarAccessFragment2 extends AbstractGeneratorFragment2 {
protected def StringConcatenationClient parserRuleClasses(ParserRule it) '''
public class «gaRuleAccessorClassName» extends «AbstractParserRuleElementFinder» {
private final «ParserRule» rule = («ParserRule») «GrammarUtil».findRuleForName(getGrammar(), "«name»");
private final «ParserRule» rule = («ParserRule») «GrammarUtil».findRuleForName(getGrammar(), "«qualifiedName»");
«FOR e : containedAbstractElements»
private final «e.eClass» «e.gaElementAccessorLocalVarName» = «e.loadElementStatement»;
«ENDFOR»
@ -263,7 +267,7 @@ class GrammarAccessFragment2 extends AbstractGeneratorFragment2 {
protected def StringConcatenationClient parserRuleClasses(EnumRule it) '''
public class «gaRuleAccessorClassName» extends «AbstractEnumRuleElementFinder» {
private final «EnumRule» rule = («EnumRule») «GrammarUtil».findRuleForName(getGrammar(), "«name»");
private final «EnumRule» rule = («EnumRule») «GrammarUtil».findRuleForName(getGrammar(), "«qualifiedName»");
«FOR e : containedAbstractElements»
private final «e.eClass» «e.gaElementAccessorLocalVarName» = «e.loadElementStatement»;
«ENDFOR»
@ -299,7 +303,7 @@ class GrammarAccessFragment2 extends AbstractGeneratorFragment2 {
'''
protected def dispatch StringConcatenationClient initializer(TerminalRule it) '''
this.«gaRuleAccessorLocalVarName» = («TerminalRule») «GrammarUtil».findRuleForName(getGrammar(), "«name»");
this.«gaRuleAccessorLocalVarName» = («TerminalRule») «GrammarUtil».findRuleForName(getGrammar(), "«qualifiedName»");
'''
protected def dispatch StringConcatenationClient getter(ParserRule it, Grammar original) '''
@ -309,8 +313,8 @@ class GrammarAccessFragment2 extends AbstractGeneratorFragment2 {
return «gaRuleAccessorLocalVarName»;
}
«ELSE»
public «grammar.grammarAccess».«gaRuleAccessorClassName» «gaElementsAccessor» {
return «usedGrammar(original).gaGrammarAccessLocalVarName».«gaElementsAccessor»;
public «grammar.grammarAccess».«gaBaseRuleAccessorClassName» «gaElementsAccessor» {
return «usedGrammar(original).gaGrammarAccessLocalVarName».«gaBaseElementsAccessor»;
}
«ENDIF»
@ -342,7 +346,7 @@ class GrammarAccessFragment2 extends AbstractGeneratorFragment2 {
«IF grammar === original»
return «gaRuleAccessorLocalVarName»;
«ELSE»
return «usedGrammar(original).gaGrammarAccessLocalVarName».«gaRuleAccessor»;
return «usedGrammar(original).gaGrammarAccessLocalVarName».«gaBaseRuleAccessor»;
«ENDIF»
}
'''

View file

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<component id="org.eclipse.xtext" version="2">
<resource path="META-INF/MANIFEST.MF">
<filter comment="version will be generated during buckminster build" id="927989779">
<filter id="927989779">
<message_arguments>
<message_argument value="2.9.0"/>
<message_argument value="org.eclipse.xtext.util"/>
@ -24,6 +24,175 @@
</message_arguments>
</filter>
</resource>
<resource path="emf-gen/org/eclipse/xtext/XtextPackage.java" type="org.eclipse.xtext.XtextPackage">
<filter id="388194388">
<message_arguments>
<message_argument value="org.eclipse.xtext.XtextPackage"/>
<message_argument value="GROUP_FEATURE_COUNT"/>
<message_argument value="4"/>
</message_arguments>
</filter>
<filter id="388194388">
<message_arguments>
<message_argument value="org.eclipse.xtext.XtextPackage"/>
<message_argument value="PARSER_RULE_FEATURE_COUNT"/>
<message_argument value="5"/>
</message_arguments>
</filter>
<filter id="388194388">
<message_arguments>
<message_argument value="org.eclipse.xtext.XtextPackage"/>
<message_argument value="RULE_CALL_FEATURE_COUNT"/>
<message_argument value="4"/>
</message_arguments>
</filter>
</resource>
<resource path="emf-gen/org/eclipse/xtext/XtextPackage.java" type="org.eclipse.xtext.XtextPackage$Literals">
<filter id="403767336">
<message_arguments>
<message_argument value="org.eclipse.xtext.XtextPackage.Literals"/>
<message_argument value="COMPOSITE_CONDITION"/>
</message_arguments>
</filter>
<filter id="403767336">
<message_arguments>
<message_argument value="org.eclipse.xtext.XtextPackage.Literals"/>
<message_argument value="COMPOSITE_CONDITION__LEFT"/>
</message_arguments>
</filter>
<filter id="403767336">
<message_arguments>
<message_argument value="org.eclipse.xtext.XtextPackage.Literals"/>
<message_argument value="COMPOSITE_CONDITION__RIGHT"/>
</message_arguments>
</filter>
<filter id="403767336">
<message_arguments>
<message_argument value="org.eclipse.xtext.XtextPackage.Literals"/>
<message_argument value="CONDITION"/>
</message_arguments>
</filter>
<filter id="403767336">
<message_arguments>
<message_argument value="org.eclipse.xtext.XtextPackage.Literals"/>
<message_argument value="CONJUNCTION"/>
</message_arguments>
</filter>
<filter id="403767336">
<message_arguments>
<message_argument value="org.eclipse.xtext.XtextPackage.Literals"/>
<message_argument value="DISJUNCTION"/>
</message_arguments>
</filter>
<filter id="403767336">
<message_arguments>
<message_argument value="org.eclipse.xtext.XtextPackage.Literals"/>
<message_argument value="GROUP__GUARD_CONDITION"/>
</message_arguments>
</filter>
<filter id="403767336">
<message_arguments>
<message_argument value="org.eclipse.xtext.XtextPackage.Literals"/>
<message_argument value="LITERAL_CONDITION"/>
</message_arguments>
</filter>
<filter id="403767336">
<message_arguments>
<message_argument value="org.eclipse.xtext.XtextPackage.Literals"/>
<message_argument value="LITERAL_CONDITION__TRUE"/>
</message_arguments>
</filter>
<filter id="403767336">
<message_arguments>
<message_argument value="org.eclipse.xtext.XtextPackage.Literals"/>
<message_argument value="NAMED_ARGUMENT"/>
</message_arguments>
</filter>
<filter id="403767336">
<message_arguments>
<message_argument value="org.eclipse.xtext.XtextPackage.Literals"/>
<message_argument value="NAMED_ARGUMENT__CALLED_BY_NAME"/>
</message_arguments>
</filter>
<filter id="403767336">
<message_arguments>
<message_argument value="org.eclipse.xtext.XtextPackage.Literals"/>
<message_argument value="NAMED_ARGUMENT__PARAMETER"/>
</message_arguments>
</filter>
<filter id="403767336">
<message_arguments>
<message_argument value="org.eclipse.xtext.XtextPackage.Literals"/>
<message_argument value="NAMED_ARGUMENT__VALUE"/>
</message_arguments>
</filter>
<filter id="403767336">
<message_arguments>
<message_argument value="org.eclipse.xtext.XtextPackage.Literals"/>
<message_argument value="NEGATION"/>
</message_arguments>
</filter>
<filter id="403767336">
<message_arguments>
<message_argument value="org.eclipse.xtext.XtextPackage.Literals"/>
<message_argument value="NEGATION__VALUE"/>
</message_arguments>
</filter>
<filter id="403767336">
<message_arguments>
<message_argument value="org.eclipse.xtext.XtextPackage.Literals"/>
<message_argument value="PARAMETER"/>
</message_arguments>
</filter>
<filter id="403767336">
<message_arguments>
<message_argument value="org.eclipse.xtext.XtextPackage.Literals"/>
<message_argument value="PARAMETER_REFERENCE"/>
</message_arguments>
</filter>
<filter id="403767336">
<message_arguments>
<message_argument value="org.eclipse.xtext.XtextPackage.Literals"/>
<message_argument value="PARAMETER_REFERENCE__PARAMETER"/>
</message_arguments>
</filter>
<filter id="403767336">
<message_arguments>
<message_argument value="org.eclipse.xtext.XtextPackage.Literals"/>
<message_argument value="PARAMETER__NAME"/>
</message_arguments>
</filter>
<filter id="403767336">
<message_arguments>
<message_argument value="org.eclipse.xtext.XtextPackage.Literals"/>
<message_argument value="PARSER_RULE__FRAGMENT"/>
</message_arguments>
</filter>
<filter id="403767336">
<message_arguments>
<message_argument value="org.eclipse.xtext.XtextPackage.Literals"/>
<message_argument value="PARSER_RULE__PARAMETERS"/>
</message_arguments>
</filter>
<filter id="403767336">
<message_arguments>
<message_argument value="org.eclipse.xtext.XtextPackage.Literals"/>
<message_argument value="PARSER_RULE__WILDCARD"/>
</message_arguments>
</filter>
<filter id="403767336">
<message_arguments>
<message_argument value="org.eclipse.xtext.XtextPackage.Literals"/>
<message_argument value="RULE_CALL__ARGUMENTS"/>
</message_arguments>
</filter>
<filter id="403767336">
<message_arguments>
<message_argument value="org.eclipse.xtext.XtextPackage.Literals"/>
<message_argument value="RULE_CALL__EXPLICITLY_CALLED"/>
</message_arguments>
</filter>
</resource>
<resource path="src-gen/org/eclipse/xtext/AbstractXtextRuntimeModule.java" type="org.eclipse.xtext.AbstractXtextRuntimeModule">
<filter comment="the parse tree constructor is no longer used" id="338792546">
<message_arguments>

View file

@ -32,6 +32,10 @@
<eStructuralFeatures xsi:type="ecore:EAttribute" name="definesHiddenTokens" eType="ecore:EDataType platform:/resource/org.eclipse.emf.ecore/model/Ecore.ecore#//EBoolean"/>
<eStructuralFeatures xsi:type="ecore:EReference" name="hiddenTokens" unique="false"
upperBound="-1" eType="#//AbstractRule"/>
<eStructuralFeatures xsi:type="ecore:EReference" name="parameters" upperBound="-1"
eType="#//Parameter" containment="true"/>
<eStructuralFeatures xsi:type="ecore:EAttribute" name="fragment" eType="ecore:EDataType platform:/resource/org.eclipse.emf.ecore/model/Ecore.ecore#//EBoolean"/>
<eStructuralFeatures xsi:type="ecore:EAttribute" name="wildcard" eType="ecore:EDataType platform:/resource/org.eclipse.emf.ecore/model/Ecore.ecore#//EBoolean"/>
</eClassifiers>
<eClassifiers xsi:type="ecore:EClass" name="TypeRef">
<eStructuralFeatures xsi:type="ecore:EReference" name="metamodel" eType="#//AbstractMetamodelDeclaration"/>
@ -53,6 +57,9 @@
</eClassifiers>
<eClassifiers xsi:type="ecore:EClass" name="RuleCall" eSuperTypes="#//AbstractElement">
<eStructuralFeatures xsi:type="ecore:EReference" name="rule" eType="#//AbstractRule"/>
<eStructuralFeatures xsi:type="ecore:EReference" name="arguments" upperBound="-1"
eType="#//NamedArgument" containment="true"/>
<eStructuralFeatures xsi:type="ecore:EAttribute" name="explicitlyCalled" eType="ecore:EDataType platform:/resource/org.eclipse.emf.ecore/model/Ecore.ecore#//EBoolean"/>
</eClassifiers>
<eClassifiers xsi:type="ecore:EClass" name="Assignment" eSuperTypes="#//AbstractElement">
<eStructuralFeatures xsi:type="ecore:EAttribute" name="feature" eType="ecore:EDataType platform:/resource/org.eclipse.emf.ecore/model/Ecore.ecore#//EString"/>
@ -84,7 +91,10 @@
</eClassifiers>
<eClassifiers xsi:type="ecore:EClass" name="Alternatives" eSuperTypes="#//CompoundElement"/>
<eClassifiers xsi:type="ecore:EClass" name="UnorderedGroup" eSuperTypes="#//CompoundElement"/>
<eClassifiers xsi:type="ecore:EClass" name="Group" eSuperTypes="#//CompoundElement"/>
<eClassifiers xsi:type="ecore:EClass" name="Group" eSuperTypes="#//CompoundElement">
<eStructuralFeatures xsi:type="ecore:EReference" name="guardCondition" eType="#//Condition"
containment="true"/>
</eClassifiers>
<eClassifiers xsi:type="ecore:EClass" name="CharacterRange" eSuperTypes="#//AbstractElement">
<eStructuralFeatures xsi:type="ecore:EReference" name="left" eType="#//Keyword"
containment="true"/>
@ -96,4 +106,32 @@
eType="#//AbstractElement" containment="true"/>
</eClassifiers>
<eClassifiers xsi:type="ecore:EClass" name="EOF" eSuperTypes="#//AbstractElement"/>
<eClassifiers xsi:type="ecore:EClass" name="Parameter">
<eStructuralFeatures xsi:type="ecore:EAttribute" name="name" eType="ecore:EDataType platform:/resource/org.eclipse.emf.ecore/model/Ecore.ecore#//EString"/>
</eClassifiers>
<eClassifiers xsi:type="ecore:EClass" name="NamedArgument">
<eStructuralFeatures xsi:type="ecore:EReference" name="parameter" eType="#//Parameter"/>
<eStructuralFeatures xsi:type="ecore:EReference" name="value" eType="#//Condition"
containment="true"/>
<eStructuralFeatures xsi:type="ecore:EAttribute" name="calledByName" eType="ecore:EDataType platform:/resource/org.eclipse.emf.ecore/model/Ecore.ecore#//EBoolean"/>
</eClassifiers>
<eClassifiers xsi:type="ecore:EClass" name="Condition"/>
<eClassifiers xsi:type="ecore:EClass" name="Conjunction" eSuperTypes="#//CompositeCondition"/>
<eClassifiers xsi:type="ecore:EClass" name="Negation" eSuperTypes="#//Condition">
<eStructuralFeatures xsi:type="ecore:EReference" name="value" eType="#//Condition"
containment="true"/>
</eClassifiers>
<eClassifiers xsi:type="ecore:EClass" name="Disjunction" eSuperTypes="#//CompositeCondition"/>
<eClassifiers xsi:type="ecore:EClass" name="CompositeCondition" eSuperTypes="#//Condition">
<eStructuralFeatures xsi:type="ecore:EReference" name="left" eType="#//Condition"
containment="true"/>
<eStructuralFeatures xsi:type="ecore:EReference" name="right" eType="#//Condition"
containment="true"/>
</eClassifiers>
<eClassifiers xsi:type="ecore:EClass" name="ParameterReference" eSuperTypes="#//Condition">
<eStructuralFeatures xsi:type="ecore:EReference" name="parameter" eType="#//Parameter"/>
</eClassifiers>
<eClassifiers xsi:type="ecore:EClass" name="LiteralCondition" eSuperTypes="#//Condition">
<eStructuralFeatures xsi:type="ecore:EAttribute" name="true" eType="ecore:EDataType platform:/resource/org.eclipse.emf.ecore/model/Ecore.ecore#//EBoolean"/>
</eClassifiers>
</ecore:EPackage>

View file

@ -5,7 +5,7 @@
forceOverwrite="true" modelName="Xtext" updateClasspath="false" rootExtendsClass="org.eclipse.emf.ecore.impl.MinimalEObjectImpl$Container"
importerID="org.eclipse.emf.importer.ecore" complianceLevel="5.0" copyrightFields="false"
editPluginID="org.eclipse.xtext.edit" editorPluginID="org.eclipse.xtext.editor"
usedGenPackages="../../../../org.eclipse.emf.ecore/model/Ecore.genmodel#//ecore">
usedGenPackages="platform:/resource/org.eclipse.emf.ecore/model/Ecore.genmodel#//ecore">
<foreignModel>Xtext.ecore</foreignModel>
<genPackages prefix="Xtext" basePackage="org.eclipse" disposableProviderFactory="true"
ecorePackage="Xtext.ecore#/">
@ -33,6 +33,9 @@
<genClasses ecoreClass="Xtext.ecore#//ParserRule">
<genFeatures createChild="false" ecoreFeature="ecore:EAttribute Xtext.ecore#//ParserRule/definesHiddenTokens"/>
<genFeatures notify="false" createChild="false" propertySortChoices="true" ecoreFeature="ecore:EReference Xtext.ecore#//ParserRule/hiddenTokens"/>
<genFeatures property="None" children="true" createChild="true" ecoreFeature="ecore:EReference Xtext.ecore#//ParserRule/parameters"/>
<genFeatures createChild="false" ecoreFeature="ecore:EAttribute Xtext.ecore#//ParserRule/fragment"/>
<genFeatures createChild="false" ecoreFeature="ecore:EAttribute Xtext.ecore#//ParserRule/wildcard"/>
</genClasses>
<genClasses ecoreClass="Xtext.ecore#//TypeRef">
<genFeatures notify="false" createChild="false" propertySortChoices="true" ecoreFeature="ecore:EReference Xtext.ecore#//TypeRef/metamodel"/>
@ -53,6 +56,8 @@
</genClasses>
<genClasses ecoreClass="Xtext.ecore#//RuleCall">
<genFeatures notify="false" createChild="false" propertySortChoices="true" ecoreFeature="ecore:EReference Xtext.ecore#//RuleCall/rule"/>
<genFeatures notify="false" createChild="false" propertySortChoices="true" ecoreFeature="ecore:EReference Xtext.ecore#//RuleCall/arguments"/>
<genFeatures createChild="false" ecoreFeature="ecore:EAttribute Xtext.ecore#//RuleCall/explicitlyCalled"/>
</genClasses>
<genClasses ecoreClass="Xtext.ecore#//Assignment">
<genFeatures createChild="false" ecoreFeature="ecore:EAttribute Xtext.ecore#//Assignment/feature"/>
@ -88,5 +93,17 @@
<genFeatures property="None" children="true" createChild="true" ecoreFeature="ecore:EReference Xtext.ecore#//CompoundElement/elements"/>
</genClasses>
<genClasses ecoreClass="Xtext.ecore#//EOF"/>
<genClasses ecoreClass="Xtext.ecore#//Parameter">
<genFeatures createChild="false" ecoreFeature="ecore:EAttribute Xtext.ecore#//Parameter/name"/>
</genClasses>
<genClasses ecoreClass="Xtext.ecore#//ConditionalBranch">
<genFeatures notify="false" createChild="false" propertySortChoices="true" ecoreFeature="ecore:EReference Xtext.ecore#//ConditionalBranch/parameter"/>
<genFeatures createChild="false" ecoreFeature="ecore:EAttribute Xtext.ecore#//ConditionalBranch/filtered"/>
</genClasses>
<genClasses ecoreClass="Xtext.ecore#//NamedArgument">
<genFeatures notify="false" createChild="false" propertySortChoices="true" ecoreFeature="ecore:EReference Xtext.ecore#//NamedArgument/parameter"/>
<genFeatures createChild="false" ecoreFeature="ecore:EAttribute Xtext.ecore#//NamedArgument/literalValue"/>
<genFeatures notify="false" createChild="false" propertySortChoices="true" ecoreFeature="ecore:EReference Xtext.ecore#//NamedArgument/value"/>
</genClasses>
</genPackages>
</genmodel:GenModel>

View file

@ -55,6 +55,9 @@ import org.eclipse.xtext.resource.DerivedStateAwareResource;
import org.eclipse.xtext.util.CancelIndicator;
import org.eclipse.xtext.util.Strings;
import com.google.common.collect.Iterables;
import com.google.common.collect.Iterators;
import com.google.common.collect.Lists;
import com.google.common.collect.AbstractIterator;
import com.google.common.collect.MapMaker;
@ -179,27 +182,12 @@ public class EcoreUtil2 extends EcoreUtil {
return target;
}
@SuppressWarnings("unchecked")
public static <T extends EObject> List<T> getAllContentsOfType(EObject ele, Class<T> type) {
List<T> result = new ArrayList<T>();
TreeIterator<EObject> allContents = ele.eAllContents();
while (allContents.hasNext()) {
EObject object = allContents.next();
if (type.isAssignableFrom(object.getClass())) {
result.add((T) object);
}
}
return result;
return Lists.newArrayList(Iterators.filter(ele.eAllContents(), type));
}
public static <T> List<T> typeSelect(List<?> elements, Class<T> clazz) {
List<T> result = new ArrayList<T>();
for (Object ele : elements) {
if (ele != null && clazz.isAssignableFrom(ele.getClass())) {
result.add(clazz.cast(ele));
}
}
return result;
return Lists.newArrayList(Iterables.filter(elements, clazz));
}
public static <T> List<T> collect(Collection<? extends EObject> instances, int featureId, Class<T> type) {
@ -217,19 +205,8 @@ public class EcoreUtil2 extends EcoreUtil {
return result;
}
@SuppressWarnings("unchecked")
public static <T extends EObject> List<T> eAllOfType(EObject ele, Class<T> type) {
List<T> result = new ArrayList<T>();
if (type.isAssignableFrom(ele.getClass()))
result.add((T) ele);
TreeIterator<EObject> allContents = ele.eAllContents();
while (allContents.hasNext()) {
EObject object = allContents.next();
if (type.isAssignableFrom(object.getClass())) {
result.add((T) object);
}
}
return result;
return Lists.newArrayList(Iterators.filter(eAll(ele), type));
}
public static TreeIterator<EObject> eAll(final EObject obj) {
@ -291,20 +268,11 @@ public class EcoreUtil2 extends EcoreUtil {
}
public static List<EObject> eAllContentsAsList(EObject ele) {
List<EObject> result = new ArrayList<EObject>();
TreeIterator<EObject> iterator = ele.eAllContents();
while (iterator.hasNext())
result.add(iterator.next());
return result;
return Lists.newArrayList(ele.eAllContents());
}
public static List<EObject> eAllContentsAsList(Resource resource) {
List<EObject> result = new ArrayList<EObject>();
TreeIterator<EObject> iterator = resource.getAllContents();
while (iterator.hasNext()) {
result.add(iterator.next());
}
return result;
return Lists.newArrayList(resource.getAllContents());
}
public static final EPackage loadEPackage(String uriAsString, ClassLoader classLoader) {
@ -412,10 +380,11 @@ public class EcoreUtil2 extends EcoreUtil {
}
private static void collectAllSuperTypes(Set<EClass> collectedTypes, EClass eClass) {
for (EClass superType : eClass.getESuperTypes())
for (EClass superType : eClass.getESuperTypes()) {
if (collectedTypes.add(superType)) {
collectAllSuperTypes(collectedTypes, superType);
}
}
}
/**

View file

@ -15,6 +15,8 @@ import static org.eclipse.xtext.EcoreUtil2.*;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
@ -39,6 +41,8 @@ import org.eclipse.xtext.xtext.CurrentTypeFinder;
import com.google.common.base.Function;
import com.google.common.base.Predicate;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
/**
* @author Jan Koehnlein
@ -179,8 +183,7 @@ public class GrammarUtil {
public static boolean isEObjectRuleCall(EObject grammarElement) {
if (grammarElement instanceof RuleCall) {
AbstractRule calledRule = ((RuleCall) grammarElement).getRule();
return calledRule != null && calledRule instanceof ParserRule
&& calledRule.getType().getClassifier() instanceof EClass;
return isEObjectRule(calledRule);
}
return false;
}
@ -189,8 +192,13 @@ public class GrammarUtil {
* @since 2.0
*/
public static boolean isEObjectRule(EObject grammarElement) {
return grammarElement instanceof ParserRule
&& ((ParserRule) grammarElement).getType().getClassifier() instanceof EClass;
if (grammarElement instanceof ParserRule) {
ParserRule rule = (ParserRule) grammarElement;
TypeRef type = rule.getType();
// wildcard fragments are considered to be EObjectRules, too
return type == null || type.getClassifier() instanceof EClass;
}
return false;
}
/**
@ -211,6 +219,24 @@ public class GrammarUtil {
return GrammarUtil.containingAssignment(ele) == null;
}
/**
* @since 2.9
*/
public static boolean isEObjectFragmentRuleCall(EObject ele) {
if (ele instanceof RuleCall) {
AbstractRule rule = ((RuleCall) ele).getRule();
return isEObjectFragmentRule(rule);
}
return false;
}
/**
* @since 2.9
*/
public static boolean isEObjectFragmentRule(AbstractRule rule) {
return rule instanceof ParserRule && ((ParserRule) rule).isFragment();
}
/**
* @since 2.0
*/
@ -224,7 +250,7 @@ public class GrammarUtil {
if (grammarElement instanceof RuleCall) {
AbstractRule calledRule = ((RuleCall) grammarElement).getRule();
return calledRule != null && calledRule instanceof ParserRule
&& calledRule.getType().getClassifier() instanceof EDataType;
&& calledRule.getType() != null && calledRule.getType().getClassifier() instanceof EDataType;
}
return false;
}
@ -248,56 +274,102 @@ public class GrammarUtil {
return false;
}
/**
* @param ruleName
* the name of the rule that should be found. May be a qualified name with a dot as a delimiter.
*/
public static AbstractRule findRuleForName(Grammar grammar, String ruleName) {
if (ruleName == null)
return null;
for (AbstractRule rule : grammar.getRules()) {
if (ruleName.equals(rule.getName())) {
return rule;
}
int lastIndex = ruleName.lastIndexOf('.');
if (lastIndex == -1) {
return findRuleForNameRecursively(grammar, null, ruleName, Sets.<Grammar>newHashSet());
} else {
return findRuleForNameRecursively(grammar, ruleName.substring(0, lastIndex), ruleName.substring(lastIndex + 1), Sets.<Grammar>newHashSet());
}
for (Grammar usedGrammar : grammar.getUsedGrammars()) {
AbstractRule rule = findRuleForName(usedGrammar, ruleName);
if (rule != null) {
return rule;
}
private static AbstractRule findRuleForNameRecursively(Grammar grammar, String langName, String ruleName, Set<Grammar> visited) {
if (visited.add(grammar)) {
if (langName == null || langName.equals(grammar.getName())) {
for (AbstractRule rule : grammar.getRules()) {
if (ruleName.equals(rule.getName())) {
return rule;
}
}
if (langName != null) {
return null;
}
}
for (Grammar usedGrammar : grammar.getUsedGrammars()) {
AbstractRule rule = findRuleForNameRecursively(usedGrammar, langName, ruleName, visited);
if (rule != null) {
return rule;
}
}
}
return null;
}
public static List<Grammar> allUsedGrammars(Grammar grammar) {
List<Grammar> grammars = new ArrayList<Grammar>();
collectAllUsedGrammars(grammars, grammar);
return grammars;
Collection<Grammar> visitedGrammars = new LinkedHashSet<Grammar>();
for(Grammar used: grammar.getUsedGrammars())
collectAllUsedGrammars(used, grammar, visitedGrammars);
return new ArrayList<Grammar>(visitedGrammars);
}
private static void collectAllUsedGrammars(List<Grammar> grammars, Grammar grammar) {
grammars.addAll(grammar.getUsedGrammars());
for (Grammar g : grammar.getUsedGrammars())
collectAllUsedGrammars(grammars, g);
private static void collectAllUsedGrammars(Grammar grammar, Grammar start, Collection<Grammar> result) {
if (grammar == start || !result.add(grammar))
return;
for(Grammar usedGrammar: grammar.getUsedGrammars()) {
collectAllUsedGrammars(usedGrammar, start, result);
}
}
public static List<AbstractRule> allRules(Grammar grammar) {
final List<AbstractRule> result = new ArrayList<AbstractRule>();
final Set<String> names = new HashSet<String>();
final Set<Grammar> grammars = new HashSet<Grammar>();
collectAllRules(grammar, result, grammars, names);
return result;
final Set<AbstractRule> result = Sets.newLinkedHashSet();
final Set<AbstractRule> explicitlyCalled = Sets.newHashSet();
final Set<String> seenNames = Sets.newHashSet();
final Set<Grammar> seenGrammars = Sets.newHashSet();
collectAllRules(grammar, result, explicitlyCalled, seenNames, seenGrammars);
return Lists.newArrayList(result);
}
private static void collectAllRules(Grammar grammar, List<AbstractRule> result, Set<Grammar> visitedGrammars,
Set<String> knownRulenames) {
if (!visitedGrammars.add(grammar))
private static void collectAllRules(
Grammar grammar,
Set<AbstractRule> result,
Set<AbstractRule> explicitlyCalled,
Set<String> seenNames,
Set<Grammar> seenGrammars) {
if (!seenGrammars.add(grammar))
return;
for (AbstractRule rule : grammar.getRules()) {
if (knownRulenames.add(rule.getName())) {
// we need to iterate the rules twice in case an explicit call to a
// rule is made from a rule defined later
// this explicitly called rule needs to be added
// to the set of all rules even though it may
// have been specialized in the sub grammar
if (!seenNames.contains(rule.getName()) || explicitlyCalled.contains(rule)) {
AbstractElement body = rule.getAlternatives();
if (body != null) {
Iterator<EObject> contents = eAll(body);
while(contents.hasNext()) {
EObject content = contents.next();
if (content instanceof RuleCall) {
AbstractRule calledRule = ((RuleCall) content).getRule();
explicitlyCalled.add(calledRule);
}
}
}
}
}
for (AbstractRule rule : grammar.getRules()) {
if (seenNames.add(rule.getName()) || explicitlyCalled.contains(rule)) {
result.add(rule);
}
}
for (Grammar usedGrammar : grammar.getUsedGrammars()) {
collectAllRules(usedGrammar, result, visitedGrammars, knownRulenames);
collectAllRules(usedGrammar, result, explicitlyCalled, seenNames, seenGrammars);
}
}
@ -349,8 +421,13 @@ public class GrammarUtil {
final BidiIterator<INode> leafNodes = node.getAsTreeIterable().iterator();
while (leafNodes.hasPrevious()) {
INode previous = leafNodes.previous();
if (previous instanceof ILeafNode && !((ILeafNode) previous).isHidden())
return previous.getText();
if (previous instanceof ILeafNode && !((ILeafNode) previous).isHidden()) {
String result = previous.getText();
if (result != null && result.startsWith("^")) {
result = result.substring(1);
}
return result;
}
}
}
return null;

View file

@ -0,0 +1,256 @@
/*******************************************************************************
* Copyright (c) 2015 itemis AG (http://www.itemis.eu) and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*******************************************************************************/
package org.eclipse.xtext;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.eclipse.emf.common.notify.impl.AdapterImpl;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.util.EcoreUtil;
import com.google.common.collect.BiMap;
import com.google.common.collect.ImmutableBiMap;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableListMultimap;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Iterables;
import com.google.common.collect.ListMultimap;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import com.google.inject.Inject;
import com.google.inject.Singleton;
/**
* Different kinds of name mapping for rules in a grammar.
*
* @author Sebastian Zarnekow - Initial contribution and API
* @since 2.9
*/
@Singleton
public class RuleNames {
static class Adapter extends AdapterImpl {
private RuleNames ruleNames;
Adapter(RuleNames ruleNames) {
this.ruleNames = ruleNames;
}
@Override
public boolean isAdapterForType(Object type) {
return RuleNames.class.equals(type);
}
public RuleNames getRuleNames() {
return ruleNames;
}
}
private final Grammar contextGrammar;
/**
* All rules by simple name.
*/
private final ListMultimap<String, AbstractRule> simpleNameToRules;
/**
* A mapping from qualified name to rule.
*/
private final Map<String, AbstractRule> qualifiedNameToRule;
/**
* The name that is unique or qualified depending on the occurrence.
* It may either be a mapping from {@code ID} to rule or {@code com.acme.ID} to
* rule depending on the fact if the ID rule is overridden in the context grammar.
*/
private final BiMap<String, AbstractRule> nameToRule;
/**
* The name that is used in code, e.g. the grammar access method names.
*/
private final BiMap<String, AbstractRule> uniqueNameToRule;
/**
* The name that is used in the generated Antlr grammar.
*/
private final BiMap<String, AbstractRule> antlrNameToRule;
private final List<AbstractRule> allRules;
public static RuleNames getRuleNames(Grammar grammar, boolean cache) {
if (cache) {
Adapter adapter = (Adapter) EcoreUtil.getAdapter(grammar.eAdapters(), RuleNames.class);
if (adapter == null) {
return new RuleNames(grammar, true);
}
return adapter.getRuleNames();
}
return new RuleNames(grammar, false);
}
public static RuleNames getRuleNames(AbstractRule rule) {
Adapter adapter = (Adapter) EcoreUtil.getAdapter(rule.eAdapters(), RuleNames.class);
if (adapter == null) {
throw new IllegalStateException("Cannot find adapter");
}
return adapter.getRuleNames();
}
public static void ensureAdapterInstalled(Grammar grammar) {
getRuleNames(grammar, true);
}
@Inject
public RuleNames(IGrammarAccess grammarAccess) {
this(grammarAccess.getGrammar(), false);
}
public RuleNames(Grammar grammar, boolean installAdapter) {
this.contextGrammar = grammar;
Adapter adapter = new Adapter(this);
if (installAdapter) {
installAdapterIfMissing(adapter, grammar);
}
List<AbstractRule> allRules = GrammarUtil.allRules(grammar);
ImmutableListMultimap.Builder<String, AbstractRule> simpleNameToRulesBuilder = ImmutableListMultimap.builder();
ImmutableMap.Builder<String, AbstractRule> qualifiedNameToRuleBuilder = ImmutableMap.builder();
ImmutableBiMap.Builder<String, AbstractRule> uniqueNameToRuleBuilder = ImmutableBiMap.builder();
ImmutableBiMap.Builder<String, AbstractRule> antlrNameToRuleBuilder = ImmutableBiMap.builder();
Map<String, AbstractRule> names = Maps.newHashMap();
Set<String> usedAntlrNames = Sets.newHashSet();
Set<String> usedUniqueNames = Sets.newHashSet();
for(AbstractRule rule: allRules) {
String name = rule.getName();
simpleNameToRulesBuilder.put(name, rule);
String qualifiedName = getQualifiedName(rule);
qualifiedNameToRuleBuilder.put(qualifiedName, rule);
String uniqueName = name;
String antlrRuleName;
if (names.containsKey(name)) {
name = qualifiedName;
uniqueName = getInheritedUniqueName(rule, usedUniqueNames);
antlrRuleName = getInheritedAntlrRuleName(rule, usedAntlrNames);
} else {
antlrRuleName = getDefaultAntlrRuleName(rule);
}
names.put(name, rule);
if (!usedUniqueNames.add(uniqueName)) {
throw new IllegalStateException(uniqueName);
}
uniqueNameToRuleBuilder.put(uniqueName, rule);
if (!usedAntlrNames.add(antlrRuleName)) {
throw new IllegalStateException(antlrRuleName);
}
antlrNameToRuleBuilder.put(antlrRuleName, rule);
if (installAdapter) {
installAdapterIfMissing(adapter, rule);
}
}
simpleNameToRules = simpleNameToRulesBuilder.build();
qualifiedNameToRule = qualifiedNameToRuleBuilder.build();
nameToRule = ImmutableBiMap.copyOf(names);
uniqueNameToRule = uniqueNameToRuleBuilder.build();
antlrNameToRule = antlrNameToRuleBuilder.build();
this.allRules = ImmutableList.copyOf(allRules);
}
private void installAdapterIfMissing(Adapter adapter, EObject context) {
if (EcoreUtil.getAdapter(context.eAdapters(), RuleNames.class) == null) {
context.eAdapters().add(adapter);
} else {
throw new IllegalStateException("Duplicate adapter");
}
}
public List<AbstractRule> getRulesBySimpleName(String name) {
return simpleNameToRules.get(name);
}
public AbstractRule getRuleByQualifiedName(String name) {
return qualifiedNameToRule.get(name);
}
public String getQualifiedName(AbstractRule rule) {
return GrammarUtil.getGrammar(rule).getName() + "." + rule.getName();
}
public String getUniqueRuleName(AbstractRule rule) {
return uniqueNameToRule.inverse().get(rule);
}
public AbstractRule getRuleByUniqueName(String uniqueName) {
return uniqueNameToRule.get(uniqueName);
}
public String getAntlrRuleName(AbstractRule rule) {
return antlrNameToRule.inverse().get(rule);
}
public AbstractRule getRuleByAntlrName(String name) {
return antlrNameToRule.get(name);
}
public String getBestRuleName(AbstractRule rule) {
return nameToRule.inverse().get(rule);
}
public Grammar getContextGrammar() {
return contextGrammar;
}
public List<AbstractRule> getAllRules() {
return allRules;
}
public Iterable<ParserRule> getAllParserRules() {
return Iterables.filter(allRules, ParserRule.class);
}
private String getInheritedAntlrRuleName(AbstractRule rule, Set<String> usedNames) {
if (rule instanceof ParserRule || rule instanceof EnumRule) {
String candidate = "super" + rule.getName();
int i = 1;
while(usedNames.contains(candidate)) {
candidate = "super" + i + rule.getName();
i++;
}
return candidate;
}
if (rule instanceof TerminalRule) {
String candidate = "SUPER_" + rule.getName();
int i = 1;
while(usedNames.contains(candidate)) {
candidate = "SUPER_" + i + "_" + rule.getName();
i++;
}
return candidate;
}
throw new IllegalArgumentException(rule.eClass().getName());
}
private String getInheritedUniqueName(AbstractRule rule, Set<String> usedNames) {
String grammarName = GrammarUtil.getName(GrammarUtil.getGrammar(rule));
String candidate = grammarName + rule.getName();
int i = 1;
while(usedNames.contains(candidate)) {
candidate = grammarName + i + rule.getName();
i++;
}
return candidate;
}
private String getDefaultAntlrRuleName(AbstractRule rule) {
if (rule instanceof ParserRule || rule instanceof EnumRule) {
return "rule" + rule.getName();
}
if (rule instanceof TerminalRule) {
return "RULE_" + rule.getName().toUpperCase();
}
throw new IllegalArgumentException(rule.eClass().getName());
}
}

View file

@ -12,13 +12,13 @@ import "http://www.eclipse.org/emf/2002/Ecore" as ecore
Grammar:
'grammar' name=GrammarID ('with' usedGrammars+=[Grammar|GrammarID] (',' usedGrammars+=[Grammar|GrammarID])*)?
(definesHiddenTokens?='hidden' '(' (hiddenTokens+=[AbstractRule] (',' hiddenTokens+=[AbstractRule])*)? ')')?
(definesHiddenTokens?='hidden' '(' (hiddenTokens+=[AbstractRule|RuleID] (',' hiddenTokens+=[AbstractRule|RuleID])*)? ')')?
metamodelDeclarations+=AbstractMetamodelDeclaration*
(rules+=AbstractRule)+
;
GrammarID returns ecore::EString:
ID ('.' ID)*;
ValidID ('.' ValidID)*;
AbstractRule : ParserRule | TerminalRule | EnumRule;
@ -28,25 +28,46 @@ AbstractMetamodelDeclaration :
// constraint: typeSelect(GeneratedMetamodel).size() == typeSelect(GeneratedMetamodel).alias.size()
// generated metamodels have to have different aliases
GeneratedMetamodel :
'generate' name=ID ePackage=[ecore::EPackage|STRING] ('as' alias=ID)?;
'generate' name=ValidID ePackage=[ecore::EPackage|STRING] ('as' alias=ValidID)?;
// referenced metamodels may share aliases with other referenced metamodels
// and with generated metamodels
ReferencedMetamodel :
'import' ePackage=[ecore::EPackage|STRING] ('as' alias=ID)?;
'import' ePackage=[ecore::EPackage|STRING] ('as' alias=ValidID)?;
//fragment Alias returns AbstractMetamodelDeclaration:
// 'as' alias=ValidID
//;
ParserRule :
name=ID ('returns' type=TypeRef)? (definesHiddenTokens?='hidden' '(' (hiddenTokens+=[AbstractRule] (',' hiddenTokens+=[AbstractRule])*)? ')')?':'
(
^fragment?='fragment' RuleNameAndParams (wildcard?='*' | ('returns' type=TypeRef)?)
| RuleNameAndParams ('returns' type=TypeRef)?
)
(definesHiddenTokens?='hidden' '(' (hiddenTokens+=[AbstractRule|RuleID] (',' hiddenTokens+=[AbstractRule|RuleID])*)? ')')? ':'
alternatives=Alternatives
';'
;
fragment RuleNameAndParams returns ParserRule:
name=ValidID ('<' (parameters+=Parameter (',' parameters+=Parameter)*)? '>')?
;
Parameter:
name=ID
;
TypeRef :
(metamodel=[AbstractMetamodelDeclaration] '::')? classifier=[ecore::EClassifier]
;
Alternatives returns AbstractElement:
UnorderedGroup ({Alternatives.elements+=current} ('|' elements+=UnorderedGroup)+)?
ConditionalBranch ({Alternatives.elements+=current} ('|' elements+=ConditionalBranch)+)?
;
ConditionalBranch returns AbstractElement:
UnorderedGroup
| {Group} '<' guardCondition=Disjunction '>' (elements+=AbstractToken)+
;
UnorderedGroup returns AbstractElement:
@ -64,11 +85,11 @@ AbstractToken returns AbstractElement:
/* SuppressWarnings[potentialOverride]: Handled in CardinalityAwareEcoreFactory */
AbstractTokenWithCardinality returns AbstractElement:
(Assignment | AbstractTerminal) (cardinality=('?'|'*'|'+'))?
(Assignment | AbstractTerminal) cardinality=('?'|'*'|'+')?
;
Action returns Action:
'{' type=TypeRef ('.' feature=ID operator=('='|'+=') 'current')? '}'
'{' type=TypeRef ('.' feature=ValidID operator=('='|'+=') 'current')? '}'
;
AbstractTerminal returns AbstractElement:
@ -82,8 +103,6 @@ AbstractTerminal returns AbstractElement:
PredicatedRuleCall |
// We have to make this one explicit since the ParenthesizedElement does not
// create an object but we have to set the predicated flag
// TODO: As soon as we have an own element for parenthesized elements with
// cardinality, we should refactor this part of the grammar
PredicatedGroup
;
@ -92,7 +111,60 @@ Keyword :
;
RuleCall :
rule=[AbstractRule]
rule=[AbstractRule|RuleID] ('<' arguments+=NamedArgument (',' arguments+=NamedArgument)* '>')?
;
NamedArgument:
( parameter=[Parameter|ID] calledByName?= '=')?
( value=ConditionOrLiteral )
;
ConditionOrLiteral returns Condition:
LiteralCondition | Disjunction
;
LiteralCondition:
{LiteralCondition} (true?='true' | 'false')
;
Disjunction returns Condition:
Conjunction ({Disjunction.left=current} '|' right=Conjunction)*
;
Conjunction returns Condition:
Negation ({Conjunction.left=current} '&' right=Negation)*
;
Negation returns Condition:
Atom | {Negation} '!' value=Negation
;
Atom returns Condition:
ParameterReference | ParenthesizedCondition
;
ParenthesizedCondition returns Condition:
'(' Disjunction ')'
;
ParameterReference:
parameter=[Parameter|ID]
;
TerminalRuleCall returns RuleCall:
rule=[AbstractRule|RuleID]
;
RuleID returns ecore::EString:
ValidID ('::' ValidID)*
;
ValidID returns ecore::EString:
ID | 'true' | 'false'
;
Boolean returns ecore::EBoolean:
'true' | 'false'
;
PredicatedKeyword returns Keyword:
@ -100,11 +172,11 @@ PredicatedKeyword returns Keyword:
;
PredicatedRuleCall returns RuleCall:
(predicated?='=>' | firstSetPredicated?='->') rule=[AbstractRule]
(predicated?='=>' | firstSetPredicated?='->') rule=[AbstractRule] ('<' arguments+=NamedArgument (',' arguments+=NamedArgument)* '>')?
;
Assignment returns Assignment:
(predicated?='=>' | firstSetPredicated?='->')? feature=ID operator=('+='|'='|'?=') ^terminal=AssignableTerminal
(predicated?='=>' | firstSetPredicated?='->')? feature=ValidID operator=('+='|'='|'?=') ^terminal=AssignableTerminal
;
AssignableTerminal returns AbstractElement:
@ -136,7 +208,7 @@ PredicatedGroup returns Group:
;
TerminalRule :
'terminal' (^fragment?='fragment' name=ID | name=ID ('returns' type=TypeRef)?) ':'
'terminal' (^fragment?='fragment' name=ValidID | name=ValidID ('returns' type=TypeRef)?) ':'
alternatives=TerminalAlternatives
';'
;
@ -151,11 +223,11 @@ TerminalGroup returns AbstractElement:
/* SuppressWarnings[potentialOverride]: Handled in CardinalityAwareEcoreFactory */
TerminalToken returns AbstractElement:
TerminalTokenElement (cardinality=('?'|'*'|'+'))?
TerminalTokenElement cardinality=('?'|'*'|'+')?
;
TerminalTokenElement returns AbstractElement:
CharacterRange | RuleCall | ParenthesizedTerminalElement | AbstractNegatedToken | Wildcard | ^EOF
CharacterRange | TerminalRuleCall | ParenthesizedTerminalElement | AbstractNegatedToken | Wildcard | ^EOF
;
ParenthesizedTerminalElement returns AbstractElement:
@ -187,7 +259,7 @@ CharacterRange returns AbstractElement:
;
EnumRule:
'enum' name=ID ('returns' type=TypeRef)? ':'
'enum' name=ValidID ('returns' type=TypeRef)? ':'
alternatives=EnumLiterals
';'
;

View file

@ -31,6 +31,17 @@ public class DefaultTerminalConverters extends AbstractDeclarativeValueConverter
return idValueConverter;
}
@Inject
private AbstractIDValueConverter terminalsIdValueConverter;
/**
* @since 2.9
*/
@ValueConverter(rule = "org.eclipse.xtext.common.Terminals.ID")
public IValueConverter<String> TerminalsID() {
return terminalsIdValueConverter;
}
@Inject
private INTValueConverter intValueConverter;
@ -38,6 +49,17 @@ public class DefaultTerminalConverters extends AbstractDeclarativeValueConverter
public IValueConverter<Integer> INT() {
return intValueConverter;
}
@Inject
private INTValueConverter terminalsIntValueConverter;
/**
* @since 2.9
*/
@ValueConverter(rule = "org.eclipse.xtext.common.Terminals.INT")
public IValueConverter<Integer> TerminalsINT() {
return terminalsIntValueConverter;
}
@Inject
private STRINGValueConverter stringValueConverter;
@ -47,4 +69,15 @@ public class DefaultTerminalConverters extends AbstractDeclarativeValueConverter
return stringValueConverter;
}
@Inject
private STRINGValueConverter terminalsStringValueConverter;
/**
* @since 2.9
*/
@ValueConverter(rule = "org.eclipse.xtext.common.Terminals.STRING")
public IValueConverter<String> TerminalsSTRING() {
return terminalsStringValueConverter;
}
}

View file

@ -14,9 +14,12 @@ import org.eclipse.xtext.conversion.ValueConverter;
import org.eclipse.xtext.conversion.impl.AbstractNullSafeConverter;
import org.eclipse.xtext.nodemodel.INode;
import com.google.inject.Singleton;
/**
* @author Jan Koehnlein - Initial contribution and API
*/
@Singleton
public class Ecore2XtextTerminalConverters extends DefaultTerminalConverters {
private static final Pattern ID_PATTERN = Pattern.compile("\\p{Alpha}\\w*");

View file

@ -12,13 +12,22 @@ import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.eclipse.xtext.conversion.impl.AbstractDeclarativeValueConverterService;
/**
* @author Sven Efftinge - Initial contribution and API
*
* Annotate methods in your own {@link AbstractDeclarativeValueConverterService}
* to mark them as contributions to all known value converters.
*
* @author Sven Efftinge - Initial contribution and API
*/
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD, ElementType.FIELD})
public @interface ValueConverter {
/**
* The name of the rule that is converted by this value converter.
* May be a qualified name, e.g. {@code com.acme.MySuperLang.MyDataTypeRule} to
* provide value converters for inherited rules that are called with the explicit
* notation {@code super::MyDataTypeRule} or {@code com::acme::MySuperLang::MyDataTypeRule}.
*/
String rule();
}

View file

@ -10,6 +10,7 @@ package org.eclipse.xtext.conversion.impl;
import static org.eclipse.xtext.GrammarUtil.*;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Map;
import java.util.Set;
@ -22,6 +23,7 @@ import org.eclipse.xtext.Grammar;
import org.eclipse.xtext.GrammarUtil;
import org.eclipse.xtext.IGrammarAccess;
import org.eclipse.xtext.ParserRule;
import org.eclipse.xtext.RuleNames;
import org.eclipse.xtext.TerminalRule;
import org.eclipse.xtext.conversion.IValueConverter;
import org.eclipse.xtext.conversion.IValueConverterService;
@ -55,6 +57,9 @@ public abstract class AbstractDeclarativeValueConverterService extends AbstractV
@Inject
protected DefaultTerminalConverter.Factory defaultTerminalConverterFactory;
@Inject
private RuleNames ruleNames;
@Inject
public void setGrammar(IGrammarAccess grammarAccess) {
this.grammar = grammarAccess.getGrammar();
@ -105,33 +110,48 @@ public abstract class AbstractDeclarativeValueConverterService extends AbstractV
* @nooverride This method is not intended to be re-implemented or extended by clients.
* @noreference This method is not intended to be referenced by clients.
*/
@SuppressWarnings("unchecked")
protected void internalRegisterForClass(Class<?> clazz, Map<String, IValueConverter<Object>> converters) {
recursiveRegisterForClass(clazz, converters);
registerEFactoryConverters(converters);
}
/**
* @since 2.9
*/
protected void recursiveRegisterForClass(Class<?> clazz, Map<String, IValueConverter<Object>> converters) {
Method[] methods = clazz.getDeclaredMethods();
Set<String> thisConverters = Sets.newHashSet();
recursiveRegisterForClass(methods, thisConverters, true, converters);
recursiveRegisterForClass(methods, thisConverters, false, converters);
if (clazz.getSuperclass() != null) {
recursiveRegisterForClass(clazz.getSuperclass(), converters);
}
}
private void recursiveRegisterForClass(Method[] methods, Set<String> thisConverters, boolean onlyExplicitConverters,
Map<String, IValueConverter<Object>> converters) {
for (Method method : methods) {
if (isConfigurationMethod(method)) {
try {
String ruleName = method.getAnnotation(ValueConverter.class).rule();
AbstractRule rule = GrammarUtil.findRuleForName(getGrammar(), ruleName);
if (rule != null) {
if (rule instanceof TerminalRule) {
if (((TerminalRule) rule).isFragment())
throw new IllegalStateException("Tried to register a value converter for a fragment terminal rule: '" + ruleName + "'");
if (onlyExplicitConverters ^ ruleName.indexOf('.') < 0) {
AbstractRule rule = GrammarUtil.findRuleForName(getGrammar(), ruleName);
if (rule != null) {
if (rule instanceof TerminalRule) {
if (((TerminalRule) rule).isFragment())
throw new IllegalStateException("Tried to register a value converter for a fragment terminal rule: '" + ruleName + "'");
}
if (!thisConverters.add(ruleName)) {
throw new IllegalStateException("Tried to register two value converters for rule '" + ruleName + "'");
}
IValueConverter<Object> converter = registerIfMissing(ruleName, rule, method, converters);
String qualifiedName = ruleNames.getQualifiedName(rule);
registerIfMissing(qualifiedName, rule, converter, method, converters);
} else {
log.trace("Tried to register value converter for rule '" + ruleName
+ "' which is not available in the grammar.");
}
if (!thisConverters.add(ruleName)) {
throw new IllegalStateException("Tried to register two value converters for rule '" + ruleName + "'");
}
if (!converters.containsKey(ruleName)) {
IValueConverter<Object> valueConverter = (IValueConverter<Object>) method.invoke(this);
if (valueConverter instanceof IValueConverter.RuleSpecific)
((IValueConverter.RuleSpecific) valueConverter).setRule(rule);
converters.put(ruleName, valueConverter);
}
} else
log.trace("Tried to register value converter for rule '" + ruleName
+ "' which is not available in the grammar.");
}
} catch (IllegalStateException e) {
throw e;
} catch (IllegalArgumentException e) {
@ -141,10 +161,35 @@ public abstract class AbstractDeclarativeValueConverterService extends AbstractV
}
}
}
if (clazz.getSuperclass() != null) {
internalRegisterForClass(clazz.getSuperclass(), converters);
}
private IValueConverter<Object> registerIfMissing(String name, AbstractRule rule, Method method,
Map<String, IValueConverter<Object>> converters) throws IllegalAccessException, InvocationTargetException {
if (!converters.containsKey(name)) {
IValueConverter<Object> valueConverter = reflectiveGetConverter(method, rule);
converters.put(name, valueConverter);
return valueConverter;
}
return null;
}
@SuppressWarnings("unchecked")
private IValueConverter<Object> reflectiveGetConverter(Method method, AbstractRule rule)
throws IllegalAccessException, InvocationTargetException {
IValueConverter<Object> valueConverter = (IValueConverter<Object>) method.invoke(this);
if (valueConverter instanceof IValueConverter.RuleSpecific)
((IValueConverter.RuleSpecific) valueConverter).setRule(rule);
return valueConverter;
}
private void registerIfMissing(String name, AbstractRule rule, IValueConverter<Object> valueConverter, Method method,
Map<String, IValueConverter<Object>> converters) throws IllegalAccessException, InvocationTargetException {
if (!converters.containsKey(name)) {
if (valueConverter == null) {
valueConverter = reflectiveGetConverter(method, rule);
}
converters.put(name, valueConverter);
}
registerEFactoryConverters(converters);
}
protected boolean isConfigurationMethod(Method method) {
@ -159,20 +204,33 @@ public abstract class AbstractDeclarativeValueConverterService extends AbstractV
*/
protected void registerEFactoryConverters(Map<String, IValueConverter<Object>> converters) {
for (ParserRule parserRule : allParserRules(getGrammar())) {
if (isDatatypeRule(parserRule) && !converters.containsKey(parserRule.getName())) {
EDataType datatype = (EDataType) parserRule.getType().getClassifier();
converters.put(parserRule.getName(), new EFactoryValueConverter(datatype));
if (isDatatypeRule(parserRule)) {
registerIfMissing(parserRule.getName(), parserRule, converters);
registerIfMissing(ruleNames.getQualifiedName(parserRule), parserRule, converters);
}
}
for (TerminalRule terminalRule : allTerminalRules(getGrammar())) {
if (!terminalRule.isFragment()) {
String terminalRuleName = terminalRule.getName();
if (!converters.containsKey(terminalRuleName)) {
converters.put(terminalRuleName, defaultTerminalConverterFactory.create(terminalRule));
}
registerIfMissing(terminalRule.getName(), terminalRule, converters);
registerIfMissing(ruleNames.getQualifiedName(terminalRule), terminalRule, converters);
}
}
}
private void registerIfMissing(String name, TerminalRule terminalRule,
Map<String, IValueConverter<Object>> converters) {
if (!converters.containsKey(name)) {
converters.put(name, defaultTerminalConverterFactory.create(terminalRule));
}
}
private void registerIfMissing(String name, ParserRule parserRule,
Map<String, IValueConverter<Object>> converters) {
if (!converters.containsKey(name)) {
EDataType datatype = (EDataType) parserRule.getType().getClassifier();
converters.put(name, new EFactoryValueConverter(datatype));
}
}
public void setDefaultTerminalConverterFactory(DefaultTerminalConverter.Factory defaultTerminalConverterFactory) {
this.defaultTerminalConverterFactory = defaultTerminalConverterFactory;

View file

@ -19,6 +19,7 @@ import org.eclipse.xtext.conversion.ValueConverterException;
import org.eclipse.xtext.parser.antlr.ITokenDefProvider;
import org.eclipse.xtext.parser.antlr.Lexer;
import org.eclipse.xtext.parser.antlr.LexerBindings;
import org.eclipse.xtext.parser.antlr.TokenTool;
import com.google.inject.Inject;
import com.google.inject.Provider;
@ -109,7 +110,7 @@ public abstract class AbstractLexerBasedConverter<T> extends AbstractValueConver
// TODO: rename to getLexerRuleName() on next API change
protected String getRuleName(Token token) {
String result = getTokenDefMap().get(token.getType());
return result.substring("RULE_".length());
return TokenTool.getLexerRuleName(result);
}
protected Map<Integer, String> getTokenDefMap() {

View file

@ -10,7 +10,6 @@ package org.eclipse.xtext.formatting.impl;
import java.util.List;
import java.util.Set;
import org.eclipse.emf.ecore.EClass;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.xtext.AbstractElement;
import org.eclipse.xtext.Action;
@ -108,8 +107,7 @@ public class MatcherState extends AbstractNFAState<MatcherState, MatcherTransiti
}
public boolean isParserRuleCall() {
return element instanceof RuleCall
&& ((RuleCall) element).getRule().getType().getClassifier() instanceof EClass;
return GrammarUtil.isEObjectRuleCall(element);
}
public boolean isParserRuleCallOptional() {

View file

@ -18,7 +18,6 @@ import java.util.Set;
import org.eclipse.emf.common.notify.Adapter;
import org.eclipse.emf.common.notify.impl.AdapterImpl;
import org.eclipse.emf.ecore.EClass;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.xtext.AbstractElement;
import org.eclipse.xtext.AbstractRule;
@ -144,8 +143,7 @@ public class AbstractNFAState<S extends INFAState<S, T>, T extends INFATransitio
addOutgoing(((Assignment) element).getTerminal(), visited, isRuleCall, loopCenter);
else if (element instanceof CrossReference)
addOutgoing(((CrossReference) element).getTerminal(), visited, isRuleCall, loopCenter);
else if (element instanceof RuleCall
&& ((RuleCall) element).getRule().getType().getClassifier() instanceof EClass) {
else if (GrammarUtil.isEObjectRuleCall(element)) {
addOutgoing(((RuleCall) element).getRule().getAlternatives(), visited, true, loopCenter);
collectOutgoingByContainer(element, visited, isRuleCall, loopCenter);
} else {
@ -213,7 +211,7 @@ public class AbstractNFAState<S extends INFAState<S, T>, T extends INFATransitio
protected boolean filter(AbstractElement ele) {
AbstractRule rule = GrammarUtil.containingRule(ele);
if (rule == null || !(rule.getType().getClassifier() instanceof EClass))
if (rule == null || !GrammarUtil.isEObjectRule(rule))
return true;
return builder.filter(ele);
}
@ -227,7 +225,7 @@ public class AbstractNFAState<S extends INFAState<S, T>, T extends INFATransitio
for (EObject root : element.eResource().getContents())
if (root instanceof Grammar)
for (AbstractRule rule : ((Grammar) root).getRules())
if (rule.getType().getClassifier() instanceof EClass)
if (GrammarUtil.isEObjectRule(rule))
for (AbstractElement ele : EcoreUtil2.eAllOfType(rule, AbstractElement.class))
if (!builder.filter(ele))
builder.getState(ele).getOutgoing();

View file

@ -169,7 +169,7 @@ public class GrammarElementTitleSwitch extends XtextSwitch<String> implements Fu
o = (o == null) ? "" : o;
String result;
if (showActionAsRuleCall && f != null) {
result = f + o + new Context2NameFunction().apply(object) + card(object);
result = f + o + new Context2NameFunction().toFunction(null).apply(object) + card(object);
} else {
String t = object.getType() != null && object.getType().getClassifier() != null ? object.getType()
.getClassifier().getName() : "null";

View file

@ -17,8 +17,11 @@ import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EReference;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.xtext.AbstractElement;
import org.eclipse.xtext.Action;
import org.eclipse.xtext.Assignment;
import org.eclipse.xtext.GrammarUtil;
import org.eclipse.xtext.ParserRule;
import org.eclipse.xtext.RuleCall;
import org.eclipse.xtext.diagnostics.IDiagnosticConsumer;
import org.eclipse.xtext.linking.lazy.LazyLinkingResource;
import org.eclipse.xtext.nodemodel.ICompositeNode;
@ -123,9 +126,17 @@ public abstract class AbstractCleaningLinker extends AbstractLinker {
EObject grammarElement = node.getGrammarElement();
if (grammarElement instanceof AbstractElement) {
ICompositeNode parent = node.getParent();
if (parent != null && !parent.hasDirectSemanticElement()) {
Assignment assignment = GrammarUtil.containingAssignment(grammarElement);
return assignment == null;
if (parent != null) {
if (!parent.hasDirectSemanticElement()) {
Assignment assignment = GrammarUtil.containingAssignment(grammarElement);
return assignment == null;
}
if (grammarElement instanceof Action) {
ParserRule rule = (ParserRule) GrammarUtil.containingRule(grammarElement);
if (rule.isFragment()) {
return parent.getGrammarElement() instanceof RuleCall;
}
}
}
}
return false;

View file

@ -18,8 +18,11 @@ import org.eclipse.emf.common.util.TreeIterator;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EReference;
import org.eclipse.emf.ecore.util.InternalEList;
import org.eclipse.xtext.AbstractRule;
import org.eclipse.xtext.CrossReference;
import org.eclipse.xtext.GrammarUtil;
import org.eclipse.xtext.ParserRule;
import org.eclipse.xtext.RuleCall;
import org.eclipse.xtext.diagnostics.DiagnosticMessage;
import org.eclipse.xtext.diagnostics.IDiagnosticConsumer;
import org.eclipse.xtext.diagnostics.IDiagnosticProducer;
@ -53,22 +56,29 @@ public class Linker extends AbstractCleaningLinker {
if (node == null)
return;
Set<EReference> handledReferences = new HashSet<EReference>();
ensureLinked(obj, producer, node, handledReferences);
ensureLinked(obj, producer, node, handledReferences, false);
producer.setNode(node);
setDefaultValues(obj, handledReferences, producer);
}
private void ensureLinked(EObject obj, IDiagnosticProducer producer, ICompositeNode node,
Set<EReference> handledReferences) {
Set<EReference> handledReferences, boolean dontCheckParent) {
for(INode abstractNode = node.getFirstChild(); abstractNode != null; abstractNode = abstractNode.getNextSibling()) {
if (abstractNode.getGrammarElement() instanceof CrossReference) {
CrossReference ref = (CrossReference) abstractNode.getGrammarElement();
EObject grammarElement = abstractNode.getGrammarElement();
if (grammarElement instanceof CrossReference) {
CrossReference ref = (CrossReference) grammarElement;
producer.setNode(abstractNode);
ensureIsLinked(obj, abstractNode, ref, handledReferences, producer);
} else if (grammarElement instanceof RuleCall && abstractNode instanceof ICompositeNode) {
RuleCall ruleCall = (RuleCall) grammarElement;
AbstractRule calledRule = ruleCall.getRule();
if (calledRule instanceof ParserRule && ((ParserRule) calledRule).isFragment()) {
ensureLinked(obj, producer, (ICompositeNode) abstractNode, handledReferences, true);
}
}
}
if (shouldCheckParentNode(node)) {
ensureLinked(obj, producer, node.getParent(), handledReferences);
if (!dontCheckParent && shouldCheckParentNode(node)) {
ensureLinked(obj, producer, node.getParent(), handledReferences, false);
}
}
@ -77,10 +87,11 @@ public class Linker extends AbstractCleaningLinker {
}
private void setDefaultValues(EObject obj, Set<EReference> references, IDiagnosticProducer producer) {
for (EReference ref : obj.eClass().getEAllReferences())
for (EReference ref : obj.eClass().getEAllReferences()) {
if (canSetDefaultValues(ref) && !references.contains(ref) && !obj.eIsSet(ref) && !ref.isDerived()) {
setDefaultValue(obj, ref, producer);
}
}
}
protected boolean canSetDefaultValues(EReference ref) {

View file

@ -28,11 +28,13 @@ import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.emf.ecore.util.EcoreUtil;
import org.eclipse.emf.ecore.util.InternalEList;
import org.eclipse.xtext.AbstractMetamodelDeclaration;
import org.eclipse.xtext.AbstractRule;
import org.eclipse.xtext.CrossReference;
import org.eclipse.xtext.EcoreUtil2;
import org.eclipse.xtext.GrammarUtil;
import org.eclipse.xtext.IGrammarAccess;
import org.eclipse.xtext.ParserRule;
import org.eclipse.xtext.RuleCall;
import org.eclipse.xtext.diagnostics.IDiagnosticConsumer;
import org.eclipse.xtext.diagnostics.IDiagnosticProducer;
import org.eclipse.xtext.linking.impl.AbstractCleaningLinker;
@ -110,11 +112,11 @@ public class LazyLinker extends AbstractCleaningLinker {
ICompositeNode node = NodeModelUtils.getNode(obj);
if (node == null)
return;
installProxies(obj, producer, settingsToLink, node);
installProxies(obj, producer, settingsToLink, node, false);
}
private void installProxies(EObject obj, IDiagnosticProducer producer,
Multimap<EStructuralFeature.Setting, INode> settingsToLink, ICompositeNode parentNode) {
Multimap<EStructuralFeature.Setting, INode> settingsToLink, ICompositeNode parentNode, boolean dontCheckParent) {
final EClass eClass = obj.eClass();
if (eClass.getEAllReferences().size() - eClass.getEAllContainments().size() == 0)
return;
@ -137,10 +139,16 @@ public class LazyLinker extends AbstractCleaningLinker {
createAndSetProxy(obj, node, eRef);
afterCreateAndSetProxy(obj, node, eRef, crossReference, producer);
}
} else if (grammarElement instanceof RuleCall && node instanceof ICompositeNode) {
RuleCall ruleCall = (RuleCall) grammarElement;
AbstractRule calledRule = ruleCall.getRule();
if (calledRule instanceof ParserRule && ((ParserRule) calledRule).isFragment()) {
installProxies(obj, producer, settingsToLink, (ICompositeNode) node, true);
}
}
}
if (shouldCheckParentNode(parentNode)) {
installProxies(obj, producer, settingsToLink, parentNode.getParent());
if (!dontCheckParent && shouldCheckParentNode(parentNode)) {
installProxies(obj, producer, settingsToLink, parentNode.getParent(), dontCheckParent);
}
}

View file

@ -24,7 +24,11 @@ import com.google.inject.Inject;
*/
public abstract class AbstractSplittingTokenSource implements TokenSource {
public static final String LEXER_RULE_PREFIX = "RULE_";
/**
* @deprecated use {@link TokenTool#LEXER_RULE_PREFIX} if strictly necessary.
*/
@Deprecated
public static final String LEXER_RULE_PREFIX = TokenTool.LEXER_RULE_PREFIX;
private TokenSource delegate;
@ -74,9 +78,7 @@ public abstract class AbstractSplittingTokenSource implements TokenSource {
}
public String getLexerRuleName(String antlrTokenDef) {
if (antlrTokenDef.startsWith(LEXER_RULE_PREFIX))
return antlrTokenDef.substring(LEXER_RULE_PREFIX.length());
return antlrTokenDef;
return TokenTool.getLexerRuleName(antlrTokenDef);
}
@Inject

View file

@ -62,7 +62,7 @@ public class AntlrTokenDefProvider implements ITokenDefProvider {
antlrTokenDef = Strings.convertFromJavaString(antlrTokenDef, true);
antlrTokenDef = "'" + antlrTokenDef + "'";
tokenDefMap.put(antlrTokenType, antlrTokenDef);
} else if (antlrTokenDef.startsWith("RULE_") || isKeywordToken(antlrTokenDef)) {
} else if (antlrTokenDef.startsWith("RULE_") || isKeywordToken(antlrTokenDef) || antlrTokenDef.startsWith("SUPER_")) {
tokenDefMap.put(antlrTokenType, antlrTokenDef);
}
line = br.readLine();

View file

@ -8,6 +8,9 @@
*******************************************************************************/
package org.eclipse.xtext.parser.antlr;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.antlr.runtime.CommonToken;
import org.antlr.runtime.Token;
@ -53,12 +56,20 @@ public class TokenTool {
public static final String LEXER_RULE_PREFIX = "RULE_";
private static final Pattern superRulePattern = Pattern.compile("^(SUPER_(\\d+_)?).*$");
public static boolean isLexerRule(String antlrTokenDef) {
return antlrTokenDef.startsWith(LEXER_RULE_PREFIX);
}
public static String getLexerRuleName(String antlrTokenDef) {
return antlrTokenDef.substring(LEXER_RULE_PREFIX.length());
if (antlrTokenDef.startsWith(LEXER_RULE_PREFIX))
return antlrTokenDef.substring(LEXER_RULE_PREFIX.length());
Matcher matcher = superRulePattern.matcher(antlrTokenDef);
if (matcher.matches()) {
return antlrTokenDef.substring(matcher.group(1).length());
}
return antlrTokenDef;
}
}

View file

@ -281,9 +281,13 @@ public class PartialParsingHelper implements IPartialParsingHelper {
return true;
if (candidate.getGrammarElement() instanceof RuleCall) {
AbstractRule rule = ((RuleCall) candidate.getGrammarElement()).getRule();
if (!(rule instanceof ParserRule) || GrammarUtil.isDatatypeRule((ParserRule) rule))
if (!(rule instanceof ParserRule))
return true;
else if (isInvalidDueToPredicates((AbstractElement) candidate.getGrammarElement()))
ParserRule casted = (ParserRule) rule;
if (GrammarUtil.isDatatypeRule(casted) || casted.isFragment() || !casted.getParameters().isEmpty()) {
return true;
}
if (isInvalidDueToPredicates((AbstractElement) candidate.getGrammarElement()))
return true;
}
if (candidate.getGrammarElement() instanceof Action) {

View file

@ -8,10 +8,10 @@
package org.eclipse.xtext.parsetree.reconstr.impl;
import org.eclipse.xtext.AbstractRule;
import org.eclipse.xtext.GrammarUtil;
import org.eclipse.xtext.IGrammarAccess;
import org.eclipse.xtext.ParserRule;
import org.eclipse.xtext.RuleNames;
import com.google.common.collect.Iterables;
import com.google.inject.Inject;
/**
@ -47,8 +47,8 @@ public class DefaultHiddenTokenHelper extends AbstractHiddenTokenHelper {
}
@Inject
private void setGrammar(IGrammarAccess grammar) {
wsRule = GrammarUtil.findRuleForName(grammar.getGrammar(), "WS");
private void setGrammar(RuleNames names) {
wsRule = Iterables.getFirst(names.getRulesBySimpleName("WS"), null);
}
}

View file

@ -20,13 +20,11 @@ import java.util.List;
import java.util.Map;
import java.util.Set;
import org.eclipse.emf.ecore.EClass;
import org.eclipse.emf.ecore.EClassifier;
import org.eclipse.xtext.AbstractElement;
import org.eclipse.xtext.Action;
import org.eclipse.xtext.Assignment;
import org.eclipse.xtext.GrammarUtil;
import org.eclipse.xtext.RuleCall;
import org.eclipse.xtext.TypeRef;
import org.eclipse.xtext.grammaranalysis.IGrammarNFAProvider.NFABuilder;
import org.eclipse.xtext.grammaranalysis.impl.AbstractNFAState;
@ -272,7 +270,7 @@ public class TreeConstState extends AbstractNFAState<TreeConstState, TreeConstTr
protected boolean isConsumingElement() {
return element instanceof Assignment
|| (element instanceof RuleCall && ((RuleCall) element).getRule().getType().getClassifier() instanceof EClass)
|| GrammarUtil.isEObjectRuleCall(element)
|| element instanceof Action;
}

View file

@ -0,0 +1,36 @@
/*******************************************************************************
* Copyright (c) 2015 itemis AG (http://www.itemis.eu) and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*******************************************************************************/
package org.eclipse.xtext.resource
import com.google.common.collect.ForwardingObject
import org.eclipse.xtend.lib.annotations.Delegate
import org.eclipse.xtend.lib.annotations.FinalFieldsConstructor
/**
* An abstract implementation of {@link IEObjectDescription} that delegates all
* method invocations to another instance. Suitable to override and specialize
* behavior.
*
* @author Sebastian Zarnekow - Initial contribution and API
* @since 2.9
*/
@FinalFieldsConstructor
abstract class ForwardingEObjectDescription extends ForwardingObject implements IEObjectDescription {
@Delegate
val IEObjectDescription delegate;
override protected delegate() {
return this.delegate
}
override toString() {
return 'ForwardingEObjectDescription[' + delegate + ']'
}
}

View file

@ -12,6 +12,7 @@ import java.io.IOException;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.xtext.AbstractRule;
import org.eclipse.xtext.Action;
import org.eclipse.xtext.Grammar;
import org.eclipse.xtext.GrammarUtil;
import org.eclipse.xtext.Keyword;
import org.eclipse.xtext.ParserRule;
@ -32,8 +33,11 @@ public class TokenStreamSequenceAdapter implements ISequenceAcceptor {
protected ITokenStream out;
public TokenStreamSequenceAdapter(ITokenStream out, ISerializationDiagnostic.Acceptor errorAcceptor) {
protected Grammar grammar;
public TokenStreamSequenceAdapter(ITokenStream out, Grammar grammar, ISerializationDiagnostic.Acceptor errorAcceptor) {
this.out = out;
this.grammar = grammar;
this.errorAcceptor = errorAcceptor;
}
@ -134,7 +138,7 @@ public class TokenStreamSequenceAdapter implements ISequenceAcceptor {
out.flush();
} catch (IOException e) {
if (errorAcceptor != null)
errorAcceptor.accept(new ExceptionDiagnostic(e));
errorAcceptor.accept(new ExceptionDiagnostic(grammar, e));
}
}
@ -165,7 +169,7 @@ public class TokenStreamSequenceAdapter implements ISequenceAcceptor {
// System.out.println("H:" + value);
} catch (IOException e) {
if (errorAcceptor != null)
errorAcceptor.accept(new ExceptionDiagnostic(e));
errorAcceptor.accept(new ExceptionDiagnostic(grammar, e));
}
}
@ -175,7 +179,7 @@ public class TokenStreamSequenceAdapter implements ISequenceAcceptor {
out.writeSemantic(grammarElement, value);
} catch (IOException e) {
if (errorAcceptor != null)
errorAcceptor.accept(new ExceptionDiagnostic(e));
errorAcceptor.accept(new ExceptionDiagnostic(grammar, e));
}
}

View file

@ -14,8 +14,10 @@ import org.eclipse.emf.ecore.EObject;
import org.eclipse.xtext.AbstractElement;
import org.eclipse.xtext.Action;
import org.eclipse.xtext.EcoreUtil2;
import org.eclipse.xtext.Grammar;
import org.eclipse.xtext.GrammarUtil;
import org.eclipse.xtext.ParserRule;
import org.eclipse.xtext.RuleNames;
import org.eclipse.xtext.util.EmfFormatter;
import com.google.common.base.Function;
@ -25,28 +27,35 @@ import com.google.common.collect.Lists;
/**
* @author Moritz Eysholdt - Initial contribution and API
*/
public class Context2NameFunction implements Function<EObject, String> {
public class Context2NameFunction {
@Override
public String apply(EObject from) {
return getContextName(from);
public Function<EObject, String> toFunction(final Grammar grammar) {
return new Function<EObject, String>() {
@Override
public String apply(EObject from) {
return getContextName(grammar, from);
}
};
}
public String getContextName(Action ctx) {
public String getContextName(Grammar grammar, Action ctx) {
ParserRule rule = EcoreUtil2.getContainerOfType(ctx, ParserRule.class);
return rule.getName() + "_" + getUniqueActionName(ctx);
return getContextName(grammar, rule) + "_" + getUniqueActionName(ctx);
}
public String getContextName(EObject ctx) {
public String getContextName(Grammar grammar, EObject ctx) {
if (GrammarUtil.isEObjectRule(ctx))
return getContextName((ParserRule) ctx);
return getContextName(grammar, (ParserRule) ctx);
if (GrammarUtil.isAssignedAction(ctx))
return getContextName((Action) ctx);
return getContextName(grammar, (Action) ctx);
throw new RuntimeException("Invalid Context: " + EmfFormatter.objPath(ctx));
}
public String getContextName(ParserRule ctx) {
return ctx.getName();
public String getContextName(Grammar grammar, ParserRule ctx) {
if (grammar == null) {
return ctx.getName();
}
return RuleNames.getRuleNames(grammar, false).getUniqueRuleName(ctx);
}
public String getUniqueActionName(Action action) {

View file

@ -10,7 +10,6 @@ package org.eclipse.xtext.serializer.analysis;
import java.util.Map;
import java.util.Set;
import org.eclipse.emf.ecore.EClass;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.xtext.AbstractElement;
import org.eclipse.xtext.Action;
@ -116,7 +115,7 @@ public class ContextPDAProvider implements IContextPDAProvider {
@Override
public AbstractElement getCall(AbstractElement ele) {
if (ele instanceof RuleCall && !GrammarUtil.isAssigned(ele)
&& ((RuleCall) ele).getRule().getType().getClassifier() instanceof EClass)
&& GrammarUtil.isEObjectRuleCall(ele))
return ((RuleCall) ele).getRule().getAlternatives();
return null;
}

View file

@ -39,7 +39,7 @@ public class ContextProvider implements IContextProvider {
protected void collectTypesForContext(TypeFinderState state, Set<EClass> types, boolean allowLocal,
boolean hasAssignment, Set<Object> visited) {
hasAssignment = hasAssignment || state.getGrammarElement() instanceof Assignment;
hasAssignment = hasAssignment || state.getGrammarElement() instanceof Assignment || GrammarUtil.isEObjectFragmentRuleCall(state.getGrammarElement());
if (allowLocal) {
if (state.getGrammarElement() instanceof Action) {
types.add((EClass) ((Action) state.getGrammarElement()).getType().getClassifier());
@ -47,10 +47,14 @@ public class ContextProvider implements IContextProvider {
}
}
if (state.isEndState() && !GrammarUtil.isUnassignedEObjectRuleCall(state.getGrammarElement())) {
if (hasAssignment)
types.add((EClass) GrammarUtil.containingRule(state.getGrammarElement()).getType().getClassifier());
else
if (hasAssignment) {
ParserRule rule = (ParserRule) GrammarUtil.containingRule(state.getGrammarElement());
if (!rule.isFragment()) {
types.add((EClass) rule.getType().getClassifier());
}
} else {
types.add(null);
}
}
if (!visited.add(state))
return;

View file

@ -32,7 +32,9 @@ import org.eclipse.xtext.Group;
import org.eclipse.xtext.Keyword;
import org.eclipse.xtext.ParserRule;
import org.eclipse.xtext.RuleCall;
import org.eclipse.xtext.RuleNames;
import org.eclipse.xtext.TerminalRule;
import org.eclipse.xtext.TypeRef;
import org.eclipse.xtext.UnorderedGroup;
import org.eclipse.xtext.grammaranalysis.impl.GrammarElementTitleSwitch;
import org.eclipse.xtext.serializer.analysis.ActionFilterNFAProvider.ActionFilterState;
@ -185,17 +187,20 @@ public class GrammarConstraintProvider implements IGrammarConstraintProvider {
case ASSIGNED_TERMINAL_RULE_CALL:
EClass type = ele.getContainingConstraint().getType();
EStructuralFeature feature = type.getEStructuralFeature(ele.getFeatureName());
if (feature == null)
throw new RuntimeException("Feature " + ele.getFeatureName() + " not found in "
if (feature == null) {
// TODO find a meaningful way to handle this
System.err.println("Feature " + ele.getFeatureName() + " not found in "
+ type.getName());
int featureID = type.getFeatureID(feature);
List<IConstraintElement> assignmentByFeature = assignmentsByFeature[featureID];
if (assignmentByFeature == null)
assignmentsByFeature[featureID] = assignmentByFeature = Lists.newArrayList();
ele.setFeatureAssignmentId(assignmentByFeature.size());
assignmentByFeature.add(ele);
ele.setAssignmentId(assignments.size());
assignments.add(ele);
} else {
int featureID = type.getFeatureID(feature);
List<IConstraintElement> assignmentByFeature = assignmentsByFeature[featureID];
if (assignmentByFeature == null)
assignmentsByFeature[featureID] = assignmentByFeature = Lists.newArrayList();
ele.setFeatureAssignmentId(assignmentByFeature.size());
assignmentByFeature.add(ele);
ele.setAssignmentId(assignments.size());
assignments.add(ele);
}
return;
case ALTERNATIVE:
case GROUP:
@ -490,10 +495,6 @@ public class GrammarConstraintProvider implements IGrammarConstraintProvider {
return false;
}
protected String context2Name(EObject context) {
return ((Constraint) getContainingConstraint()).provider.context2Name.apply(context);
}
@Override
public boolean equals(Object obj) {
if (!(obj instanceof ConstraintElement))
@ -1016,6 +1017,9 @@ public class GrammarConstraintProvider implements IGrammarConstraintProvider {
@Inject
protected Context2NameFunction context2Name;
@Inject
protected RuleNames ruleNames;
@Inject
protected IContextProvider contextProvider;
@ -1040,20 +1044,32 @@ public class GrammarConstraintProvider implements IGrammarConstraintProvider {
} else if (ele instanceof RuleCall) {
RuleCall rc = (RuleCall) ele;
if (GrammarUtil.isUnassignedEObjectRuleCall(rc)) {
if (!visited.add(rc))
return null;
ConstraintElement result = createConstraintElement((ParserRule) rc.getRule(), requiredType, visited);
if (result != null && result != INVALID) {
if (rc.getRule().getType().getClassifier() == requiredType)
result.typeMatch();
if (result.isTypeMatch())
return result;
ParserRule rule = (ParserRule) rc.getRule();
if (!rule.isFragment()) {
if (!visited.add(rc))
return null;
ConstraintElement result = createConstraintElement(rule, requiredType, visited);
if (result != null && result != INVALID) {
TypeRef returnType = rc.getRule().getType();
if (returnType.getClassifier() == requiredType)
result.typeMatch();
if (result.isTypeMatch())
return result;
}
return isOptional ? null : INVALID;
} else {
ConstraintElement result = createConstraintElement(context, rule.getAlternatives(), requiredType, visited);
if (result != null && result != INVALID) {
result.setMany(result.isMany() || GrammarUtil.isMultipleCardinality(ele));
result.setOptional(result.isOptional() || GrammarUtil.isOptionalCardinality(ele));
}
return result;
}
return isOptional ? null : INVALID;
} else if (GrammarUtil.containingAssignment(ele) != null)
} else if (GrammarUtil.containingAssignment(ele) != null) {
return new ConstraintElement(context, getConstraintElementType(ele), ele);
else
} else {
return null;
}
} else if (ele instanceof Keyword) {
if (GrammarUtil.containingAssignment(ele) != null)
return new ConstraintElement(context, getConstraintElementType(ele), ele);
@ -1235,7 +1251,7 @@ public class GrammarConstraintProvider implements IGrammarConstraintProvider {
protected ConstraintElement createConstraintElement(ParserRule rule, EClass requiredType, Set<Object> visited) {
if (!visited.add(rule))
return INVALID;
if (GrammarUtil.containsAssignedAction(rule)) {
if (GrammarUtil.containsAssignedAction(rule)) { // also check actions in used fragments
ActionFilterState start = nfaProvider.getNFA(rule.getAlternatives());
return createConstraintElement(rule, start, requiredType, false, visited);
} else {
@ -1256,7 +1272,7 @@ public class GrammarConstraintProvider implements IGrammarConstraintProvider {
for (Collection<IConstraint> equal : equalConstraints.values()) {
// Collection<IConstraint> equal = filterConstraintsFromSubGrammars(grammar, allEqual);
IConstraint representative = findRepresentativeConstraint(equal);
((Constraint) representative).setName(findBestConstraintName(equal));
((Constraint) representative).setName(findBestConstraintName(grammar, equal));
for (IConstraint constraint : equal)
allConstraints.put(constraint, representative);
}
@ -1320,7 +1336,7 @@ public class GrammarConstraintProvider implements IGrammarConstraintProvider {
collectElements(e, result);
}
protected String findBestConstraintName(Collection<IConstraint> equalConstraints) {
protected String findBestConstraintName(Grammar grammar, Collection<IConstraint> equalConstraints) {
Set<ParserRule> relevantRules = Sets.newLinkedHashSet();
Set<Action> relevantActions = Sets.newLinkedHashSet();
Set<ParserRule> contextRules = Sets.newLinkedHashSet();
@ -1366,7 +1382,7 @@ public class GrammarConstraintProvider implements IGrammarConstraintProvider {
for (Action a : relevantActions)
actions.add(context2Name.getUniqueActionName(a));
for (ParserRule a : relevantRules)
rules.add(context2Name.getContextName(a));
rules.add(context2Name.getContextName(grammar, a));
Collections.sort(rules);
String result = Joiner.on("_").join(rules);
if (!actions.isEmpty()) {
@ -1422,9 +1438,9 @@ public class GrammarConstraintProvider implements IGrammarConstraintProvider {
throw new RuntimeException("Unknown Grammar Element: " + EmfFormatter.objPath(ele));
}
protected IConstraintContext getConstraints(Action context) {
protected IConstraintContext getConstraints(Grammar grammar, Action context) {
AssignedActionConstraintContext result = new AssignedActionConstraintContext(context,
context2Name.getContextName(context));
context2Name.getContextName(grammar, context));
ActionFilterState start = nfaProvider.getNFA(context);
Set<EClass> types = contextProvider.getTypesForContext(context);
for (EClass type : types) {
@ -1440,7 +1456,8 @@ public class GrammarConstraintProvider implements IGrammarConstraintProvider {
Constraint constraint = new ActionConstraint(context, type, ce, this);
result.addConstraint(constraint);
} else
System.err.println("constraint is " + ce + " for context " + context2Name.getContextName(context)
// TODO find a meaningful way to handle this
System.err.println("constraint is " + ce + " for context " + context2Name.getContextName(grammar, context)
+ " and type " + type.getName());
}
}
@ -1452,22 +1469,23 @@ public class GrammarConstraintProvider implements IGrammarConstraintProvider {
List<IConstraintContext> result = cache.get(context);
if (result == null) {
result = Lists.newArrayList();
for (ParserRule parserRule : GrammarUtil.allParserRules(context))
if (parserRule.getType().getClassifier() instanceof EClass) {
result.add(getConstraints(parserRule));
for (ParserRule parserRule : GrammarUtil.allParserRules(context)) {
if (parserRule.getType() != null && parserRule.getType().getClassifier() instanceof EClass) {
result.add(getConstraints(context, parserRule));
for (Action action : GrammarUtil.containedActions(parserRule))
if (action.getFeature() != null)
result.add(getConstraints(action));
result.add(getConstraints(context, action));
}
}
filterDuplicateConstraintsAndSetNames(context, result);
cache.put(context, result);
}
return result;
}
protected IConstraintContext getConstraints(ParserRule context) {
protected IConstraintContext getConstraints(Grammar grammar, ParserRule context) {
ParserRuleConstraintContext result = new ParserRuleConstraintContext(context,
context2Name.getContextName(context));
context2Name.getContextName(grammar, context));
Set<EClass> types = contextProvider.getTypesForContext(context);
for (EClass type : types) {
if (type == null) {
@ -1482,7 +1500,8 @@ public class GrammarConstraintProvider implements IGrammarConstraintProvider {
Constraint constraint = new RuleConstraint(context, type, ce, this);
result.addConstraint(constraint);
} else
System.err.println("constraint is " + ce + " for context " + context2Name.getContextName(context)
// TODO find a meaningful way to handle this
System.err.println("constraint is " + ce + " for context " + context2Name.getContextName(grammar, context)
+ " and type " + type.getName());
}
}

View file

@ -25,8 +25,8 @@ import org.eclipse.xtext.Assignment;
import org.eclipse.xtext.CrossReference;
import org.eclipse.xtext.EcoreUtil2;
import org.eclipse.xtext.GrammarUtil;
import org.eclipse.xtext.IGrammarAccess;
import org.eclipse.xtext.ParserRule;
import org.eclipse.xtext.RuleNames;
import org.eclipse.xtext.grammaranalysis.impl.GrammarElementTitleSwitch;
import org.eclipse.xtext.serializer.analysis.ISyntacticSequencerPDAProvider.ISynAbsorberState;
import org.eclipse.xtext.serializer.analysis.ISyntacticSequencerPDAProvider.SynAbsorberNfaAdapter;
@ -179,7 +179,7 @@ public class SemanticSequencerNfaProvider implements ISemanticSequencerNfaProvid
protected Map<AbstractElement, Integer> elementIDCache;
@Inject
protected IGrammarAccess grammar;
protected RuleNames ruleNames;
@Inject
protected ISyntacticSequencerPDAProvider pdaProvider;
@ -200,7 +200,7 @@ public class SemanticSequencerNfaProvider implements ISemanticSequencerNfaProvid
if (elementIDCache == null) {
elementIDCache = Maps.newHashMap();
int counter = 0;
for (ParserRule pr : GrammarUtil.allParserRules(grammar.getGrammar()))
for (ParserRule pr : ruleNames.getAllParserRules())
for (AbstractElement e : EcoreUtil2.getAllContentsOfType(pr, AbstractElement.class))
elementIDCache.put(e, counter++);
}

View file

@ -12,6 +12,7 @@ import java.util.List;
import org.eclipse.emf.ecore.EClass;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EStructuralFeature;
import org.eclipse.xtext.Grammar;
import org.eclipse.xtext.serializer.analysis.Context2NameFunction;
import org.eclipse.xtext.util.EmfFormatter;
@ -33,9 +34,12 @@ public interface ISerializationDiagnostic {
public class ExceptionDiagnostic implements ISerializationDiagnostic {
protected Throwable exception;
protected Grammar grammar;
public ExceptionDiagnostic(Throwable exception) {
public ExceptionDiagnostic(Grammar grammar, Throwable exception) {
this.exception = exception;
this.grammar = grammar;
}
@Override
@ -62,6 +66,11 @@ public interface ISerializationDiagnostic {
public EObject getContext() {
return null;
}
@Override
public Grammar getGrammar() {
return grammar;
}
@Override
public String getId() {
@ -90,7 +99,7 @@ public interface ISerializationDiagnostic {
result.add("URI: " + eObject.eResource().getURI());
}
if (diagnostic.getContext() != null)
result.add("Context: " + new Context2NameFunction().getContextName(diagnostic.getContext()));
result.add("Context: " + new Context2NameFunction().getContextName(diagnostic.getGrammar(), diagnostic.getContext()));
if (diagnostic.getEStructuralFeature() != null) {
EStructuralFeature feature = diagnostic.getEStructuralFeature();
EClass eClass = feature.getEContainingClass();
@ -140,6 +149,8 @@ public interface ISerializationDiagnostic {
EObject getSemanticObject();
EObject getContext();
Grammar getGrammar();
String getId();

View file

@ -74,7 +74,7 @@ public class SequencerDiagnosticProvider implements ISemanticSequencerDiagnostic
@Override
public ISerializationDiagnostic createFeatureValueMissing(EObject semanticObject, EStructuralFeature feature) {
String msg = "A value for feature '" + feature.getName() + "' is missing but required.";
return new SerializationDiagnostic(FEATURE_VALUE_MISSING, semanticObject, msg);
return new SerializationDiagnostic(FEATURE_VALUE_MISSING, semanticObject, grammarAccess.getGrammar(), msg);
}
@Override
@ -89,18 +89,18 @@ public class SequencerDiagnosticProvider implements ISemanticSequencerDiagnostic
else
otherValidCtxs.add(ctx);
}
String contextName = context2Name.apply(context);
String contextName = context2Name.getContextName(grammarAccess.getGrammar(), context);
String semanticType = semanticObject.eClass().getName();
String recommendCtxNames = Joiner.on(", ").join(Iterables.transform(recommendedCtxs, context2Name));
String recommendCtxNames = Joiner.on(", ").join(Iterables.transform(recommendedCtxs, context2Name.toFunction(grammarAccess.getGrammar())));
String validTypeNames = Joiner.on(", ").join(Iterables.transform(validTypes, new NamedElement2Name()));
StringBuilder msg = new StringBuilder();
msg.append("The context '" + contextName + "' is not valid for type '" + semanticType + "'\n");
msg.append("Recommended contexts for type '" + semanticType + "': " + recommendCtxNames + "\n");
if (!otherValidCtxs.isEmpty())
msg.append("Other valid contexts for type '" + semanticType + "': "
+ Joiner.on(", ").join(Iterables.transform(otherValidCtxs, context2Name)));
+ Joiner.on(", ").join(Iterables.transform(otherValidCtxs, context2Name.toFunction(grammarAccess.getGrammar()))));
msg.append("The context '" + contextName + "' is valid for types: " + validTypeNames + "\n");
return new SerializationDiagnostic(INVALID_CONTEXT_OR_TYPE, semanticObject, msg.toString());
return new SerializationDiagnostic(INVALID_CONTEXT_OR_TYPE, semanticObject, grammarAccess.getGrammar(), msg.toString());
}
protected List<EObject> getValidContexts(EClass clazz) {
@ -135,7 +135,7 @@ public class SequencerDiagnosticProvider implements ISemanticSequencerDiagnostic
msg.append("Could not serialize EObject via backtracking.\n");
msg.append("Constraint: " + grammar + "\n");
msg.append(semanticObject.getValuesString());
return new SerializationDiagnostic(BACKTRACKING_FAILED, semanticObject.getEObject(), context, msg.toString());
return new SerializationDiagnostic(BACKTRACKING_FAILED, semanticObject.getEObject(), context, grammarAccess.getGrammar(), msg.toString());
}
}

View file

@ -12,6 +12,7 @@ import static org.eclipse.xtext.serializer.impl.FeatureFinderUtil.*;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EStructuralFeature;
import org.eclipse.xtext.AbstractElement;
import org.eclipse.xtext.Grammar;
/**
* @author Moritz Eysholdt - Initial contribution and API
@ -23,6 +24,8 @@ public class SerializationDiagnostic implements ISerializationDiagnostic {
protected String message;
protected EObject semanticObject;
protected Grammar grammar;
protected EObject context;
@ -30,41 +33,44 @@ public class SerializationDiagnostic implements ISerializationDiagnostic {
private Throwable throwable;
public SerializationDiagnostic(String id, EObject semantic, AbstractElement element, String msg, Throwable exc) {
this(id, semantic, semantic != null ? getFeature(element, semantic.eClass()) : null, msg, exc);
public SerializationDiagnostic(String id, EObject semantic, AbstractElement element, Grammar grammar, String msg, Throwable exc) {
this(id, semantic, semantic != null ? getFeature(element, semantic.eClass()) : null, grammar, msg, exc);
}
public SerializationDiagnostic(String id, EObject semanticObject, AbstractElement element, String message) {
this(id, semanticObject, semanticObject != null ? getFeature(element, semanticObject.eClass()) : null, message);
public SerializationDiagnostic(String id, EObject semanticObject, AbstractElement element, Grammar grammar, String message) {
this(id, semanticObject, semanticObject != null ? getFeature(element, semanticObject.eClass()) : null, grammar, message);
}
public SerializationDiagnostic(String id, EObject semanticObject, EStructuralFeature feature, String message) {
public SerializationDiagnostic(String id, EObject semanticObject, EStructuralFeature feature, Grammar grammar, String message) {
super();
this.id = id;
this.semanticObject = semanticObject;
this.grammar = grammar;
this.message = message;
this.feature = feature;
}
public SerializationDiagnostic(String id, EObject semanticObject, EStructuralFeature feature, String message, Throwable throwable) {
public SerializationDiagnostic(String id, EObject semanticObject, EStructuralFeature feature, Grammar grammar, String message, Throwable throwable) {
super();
this.id = id;
this.semanticObject = semanticObject;
this.grammar = grammar;
this.throwable = throwable;
this.feature = feature;
this.message = message;
}
public SerializationDiagnostic(String id, EObject semanticObject, EObject context, String message) {
public SerializationDiagnostic(String id, EObject semanticObject, EObject context, Grammar grammar, String message) {
super();
this.id = id;
this.semanticObject = semanticObject;
this.grammar = grammar;
this.message = message;
this.context = context;
}
public SerializationDiagnostic(String id, EObject semanticObject, String message) {
this(id, semanticObject, (EStructuralFeature) null, message);
public SerializationDiagnostic(String id, EObject semanticObject, Grammar grammar, String message) {
this(id, semanticObject, (EStructuralFeature) null, grammar, message);
}
@Override
@ -91,6 +97,11 @@ public class SerializationDiagnostic implements ISerializationDiagnostic {
public EObject getContext() {
return context;
}
@Override
public Grammar getGrammar() {
return grammar;
}
@Override
public String getId() {

View file

@ -11,6 +11,7 @@ import java.util.List;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.xtext.AbstractElement;
import org.eclipse.xtext.IGrammarAccess;
import org.eclipse.xtext.RuleCall;
import org.eclipse.xtext.grammaranalysis.impl.GrammarElementTitleSwitch;
import org.eclipse.xtext.serializer.analysis.ISyntacticSequencerPDAProvider.ISynAbsorberState;
@ -21,12 +22,16 @@ import org.eclipse.xtext.serializer.sequencer.RuleCallStack;
import com.google.common.base.Joiner;
import com.google.common.collect.Lists;
import com.google.inject.Inject;
/**
* @author Moritz Eysholdt - Initial contribution and API
*/
public class SyntacticSequencerDiagnosticProvider implements ISyntacticSequencerDiagnosticProvider {
@Inject
private IGrammarAccess grammarAccess;
@Override
public ISerializationDiagnostic createInvalidFollowingAbsorberDiagnostic(EObject context, EObject semanticObject,
ISynAbsorberState from, AbstractElement to) {
@ -42,7 +47,7 @@ public class SyntacticSequencerDiagnosticProvider implements ISyntacticSequencer
msg.append("State '" + toName + "' may not follow '" + fromName + "'.\n");
msg.append("Valid followers are: " + Joiner.on(", ").join(targets));
return new SerializationDiagnostic(INVALID_FOLLOWING_ABSORBER, semanticObject, context, msg.toString());
return new SerializationDiagnostic(INVALID_FOLLOWING_ABSORBER, semanticObject, context, grammarAccess.getGrammar(), msg.toString());
}
@Override
@ -60,6 +65,6 @@ public class SyntacticSequencerDiagnosticProvider implements ISyntacticSequencer
buf.append("Found on top of the stack: " + poppedStr + "\n");
buf.append("Expected: " + toConsume + "\n");
buf.append("Rest of the stack: " + stack + "\n");
return new SerializationDiagnostic(UNEXPECTED_STACK_TRACE, semanticObject, toConsume.getContext(), buf.toString());
return new SerializationDiagnostic(UNEXPECTED_STACK_TRACE, semanticObject, toConsume.getContext(), grammarAccess.getGrammar(), buf.toString());
}
}

View file

@ -15,6 +15,7 @@ import org.eclipse.xtext.AbstractElement;
import org.eclipse.xtext.CrossReference;
import org.eclipse.xtext.EnumLiteralDeclaration;
import org.eclipse.xtext.GrammarUtil;
import org.eclipse.xtext.IGrammarAccess;
import org.eclipse.xtext.RuleCall;
import org.eclipse.xtext.linking.impl.LinkingHelper;
import org.eclipse.xtext.scoping.IScope;
@ -31,6 +32,9 @@ public class TokenDiagnosticProvider implements ITokenDiagnosticProvider {
@Inject
private LinkingHelper linkingHelper;
@Inject
private IGrammarAccess grammarAccess;
protected String getFullReferenceName(EObject semanticObject, CrossReference reference) {
EReference ref = GrammarUtil.getReference(reference);
@ -49,7 +53,7 @@ public class TokenDiagnosticProvider implements ITokenDiagnosticProvider {
StringBuilder msg = new StringBuilder();
msg.append("The value '" + value + "' is invalid for enum " + rc.getRule().getName() + "\n");
msg.append("Valid values are: " + Joiner.on(", ").join(valid));
return new SerializationDiagnostic(INVALID_ENUM_VALUE, semanticObject, rc, msg.toString());
return new SerializationDiagnostic(INVALID_ENUM_VALUE, semanticObject, rc, grammarAccess.getGrammar(), msg.toString());
}
@Override
@ -57,19 +61,19 @@ public class TokenDiagnosticProvider implements ITokenDiagnosticProvider {
CrossReference element, EObject target, IScope scope) {
String msg = "No EObjectDescription could be found in Scope " + getFullReferenceName(semanticObject, element)
+ " for " + EmfFormatter.objPath(target);
return new SerializationDiagnostic(NO_EOBJECT_DESCRIPTION_FOUND, semanticObject, element, msg);
return new SerializationDiagnostic(NO_EOBJECT_DESCRIPTION_FOUND, semanticObject, element, grammarAccess.getGrammar(), msg);
}
@Override
public ISerializationDiagnostic getNoScopeFoundDiagnostic(EObject semanticObject, CrossReference element,
EObject target) {
String msg = "Could not create Scope for EReference " + getFullReferenceName(semanticObject, element);
return new SerializationDiagnostic(NO_SCOPE_FOUND, semanticObject, element, msg);
return new SerializationDiagnostic(NO_SCOPE_FOUND, semanticObject, element, grammarAccess.getGrammar(), msg);
}
@Override
public ISerializationDiagnostic getNullNotAllowedDiagnostic(EObject semanticObject, AbstractElement ele) {
return new SerializationDiagnostic(NULL_NOT_ALLOWED, semanticObject, ele, "Must not be null");
return new SerializationDiagnostic(NULL_NOT_ALLOWED, semanticObject, ele, grammarAccess.getGrammar(), "Must not be null");
}
@Override
@ -82,7 +86,7 @@ public class TokenDiagnosticProvider implements ITokenDiagnosticProvider {
msg.append("' for grammar rule '");
msg.append(ruleName);
msg.append("' to string via ValueConverter.");
return new SerializationDiagnostic(VALUE_CONVERSION_EXCEPTION, semantic, element, msg.toString(), exception);
return new SerializationDiagnostic(VALUE_CONVERSION_EXCEPTION, semantic, element, grammarAccess.getGrammar(), msg.toString(), exception);
}
}

View file

@ -120,7 +120,7 @@ public class Serializer implements ISerializer {
else
formatterTokenStream = formatter.createFormatterStream(null, tokenStream, !options.isFormatting());
EObject context = getContext(obj);
ISequenceAcceptor acceptor = new TokenStreamSequenceAdapter(formatterTokenStream, errors);
ISequenceAcceptor acceptor = new TokenStreamSequenceAdapter(formatterTokenStream, grammar.getGrammar(), errors);
serialize(obj, context, acceptor, errors);
formatterTokenStream.flush();
}

View file

@ -444,8 +444,10 @@ public abstract class AbstractSyntacticSequencer implements ISyntacticSequencer,
if (fromState instanceof ISynNavigable) {
ISynNavigable fromEmitter = (ISynNavigable) fromState;
// RCStack back = stack.clone();
if (fromEmitter.hasEmitters())
accept(fromNode, fromEmitter.getShortestStackpruningPathToAbsorber(stack), stack);
if (fromEmitter.hasEmitters()) {
List<ISynState> path = fromEmitter.getShortestStackpruningPathToAbsorber(stack);
accept(fromNode, path, stack);
}
return fromEmitter.getTarget();
}
return null;

View file

@ -20,7 +20,7 @@ import org.eclipse.emf.ecore.EStructuralFeature;
import org.eclipse.xtext.AbstractElement;
import org.eclipse.xtext.AbstractRule;
import org.eclipse.xtext.GrammarUtil;
import org.eclipse.xtext.IGrammarAccess;
import org.eclipse.xtext.RuleNames;
import org.eclipse.xtext.nodemodel.INode;
import org.eclipse.xtext.serializer.analysis.IGrammarConstraintProvider;
import org.eclipse.xtext.serializer.analysis.IGrammarConstraintProvider.IConstraint;
@ -55,7 +55,7 @@ public class ContextFinder implements IContextFinder {
protected Map<Pair<EObject, EClass>, IConstraint> constraints;
@Inject
protected IGrammarAccess grammar;
protected RuleNames ruleNames;
@Inject
protected IGrammarConstraintProvider grammarConstraintProvider;
@ -223,7 +223,7 @@ public class ContextFinder implements IContextFinder {
}
protected EObject getRootContext() {
for (AbstractRule rule : grammar.getGrammar().getRules())
for (AbstractRule rule : ruleNames.getAllRules())
if (GrammarUtil.isEObjectRule(rule))
return rule;
throw new RuntimeException("There is no parser rule in the grammar.");
@ -232,7 +232,7 @@ public class ContextFinder implements IContextFinder {
protected void initConstraints() {
if (constraintContexts == null) {
constraints = Maps.newLinkedHashMap();
constraintContexts = grammarConstraintProvider.getConstraints(grammar.getGrammar());
constraintContexts = grammarConstraintProvider.getConstraints(ruleNames.getContextGrammar());
// System.out.println(Joiner.on("\n").join(constraintContexts));
for (IConstraintContext ctx : constraintContexts)
for (IConstraint constraint : ctx.getConstraints())

View file

@ -14,6 +14,7 @@ import org.eclipse.xtext.GrammarUtil;
import org.eclipse.xtext.Keyword;
import org.eclipse.xtext.ParserRule;
import org.eclipse.xtext.RuleCall;
import org.eclipse.xtext.RuleNames;
import org.eclipse.xtext.TerminalRule;
import org.eclipse.xtext.conversion.IValueConverterService;
import org.eclipse.xtext.nodemodel.BidiIterator;
@ -30,6 +31,9 @@ public class NodeModelSemanticSequencer extends AbstractSemanticSequencer {
@Inject
protected IValueConverterService valueConverter;
@Inject
protected RuleNames ruleNames;
@Override
public void createSequence(EObject context, EObject semanticObject) {
@ -46,7 +50,7 @@ public class NodeModelSemanticSequencer extends AbstractSemanticSequencer {
acceptSemantic(semanticObject, rc, semanticObject.eGet(feature), node.getFirst());
} else {
String strVal = NodeModelUtils.getTokenText(node.getFirst());
Object val = valueConverter.toValue(strVal, rc.getRule().getName(), node.getFirst());
Object val = valueConverter.toValue(strVal, ruleNames.getQualifiedName(rc.getRule()), node.getFirst());
acceptSemantic(semanticObject, rc, val, node.getFirst());
}
} else if (node.getSecond() instanceof Keyword)

View file

@ -9,6 +9,7 @@ package org.eclipse.xtext.serializer.tokens;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.xtext.RuleCall;
import org.eclipse.xtext.RuleNames;
import org.eclipse.xtext.conversion.IValueConverterService;
import org.eclipse.xtext.nodemodel.INode;
import org.eclipse.xtext.nodemodel.util.NodeModelUtils;
@ -25,6 +26,9 @@ public class ValueSerializer implements IValueSerializer {
@Inject
private IValueConverterService converter;
@Inject
private RuleNames ruleNames;
@Inject
protected ITokenDiagnosticProvider diagnostics;
@ -35,7 +39,7 @@ public class ValueSerializer implements IValueSerializer {
@Override
public boolean isValid(EObject context, RuleCall ruleCall, Object value, Acceptor errors) {
try {
String str = converter.toString(value, ruleCall.getRule().getName());
String str = converter.toString(value, ruleNames.getQualifiedName(ruleCall.getRule()));
if (str != null)
return true;
if (errors != null)
@ -51,12 +55,12 @@ public class ValueSerializer implements IValueSerializer {
@Override
public String serializeAssignedValue(EObject context, RuleCall ruleCall, Object value, INode node, Acceptor errors) {
if (node != null) {
Object converted = converter.toValue(NodeModelUtils.getTokenText(node), ruleCall.getRule().getName(), node);
Object converted = converter.toValue(NodeModelUtils.getTokenText(node), ruleNames.getQualifiedName(ruleCall.getRule()), node);
if (converted != null && converted.equals(value))
return tokenUtil.serializeNode(node);
}
try {
String str = converter.toString(value, ruleCall.getRule().getName());
String str = converter.toString(value, ruleNames.getQualifiedName(ruleCall.getRule()));
if (str != null)
return str;
if (errors != null)

View file

@ -125,12 +125,25 @@ public class CurrentTypeFinder {
@Override
public Boolean caseRuleCall(RuleCall object) {
EClassifier wasType = currentType;
AbstractRule calledRule = object.getRule();
if (currentType == null) {
if (object.getRule() instanceof ParserRule && !GrammarUtil.isDatatypeRule((ParserRule) object.getRule())) {
TypeRef returnType = object.getRule().getType();
if (returnType != null)
currentType = returnType.getClassifier();
if (calledRule instanceof ParserRule && !GrammarUtil.isDatatypeRule((ParserRule) calledRule)) {
ParserRule parserRule = (ParserRule) calledRule;
if (parserRule.isFragment()) {
if (context.getType() != null)
currentType = context.getType().getClassifier();
if (!parserRule.isWildcard()) {
doSwitch(parserRule.getAlternatives());
}
} else {
TypeRef returnType = calledRule.getType();
if (returnType != null) {
currentType = returnType.getClassifier();
}
}
}
} else if (isFragmentButNotWildcard(calledRule)) {
doSwitch(calledRule.getAlternatives());
}
if (object == stopElement)
return true;
@ -139,6 +152,14 @@ public class CurrentTypeFinder {
return false;
}
private boolean isFragmentButNotWildcard(AbstractRule calledRule) {
if (calledRule instanceof ParserRule) {
ParserRule casted = (ParserRule) calledRule;
return casted.isFragment() && !casted.isWildcard();
}
return false;
}
protected EClassifier getCompatibleType(EClassifier a, EClassifier b, EObject context) {
if (a == null)
return b;

View file

@ -45,7 +45,7 @@ public class KeywordInspector {
List<TerminalRule> rules = GrammarUtil.allTerminalRules(grammar);
for(TerminalRule rule: rules) {
if (!rule.isFragment()) {
AbstractElement element = rule.getAlternatives();
AbstractElement element = rule.getAlternatives();
if (element instanceof Keyword && Strings.isEmpty(element.getCardinality())) {
String value = ((Keyword) element).getValue();
if (value.equals(keyword.getValue()))

View file

@ -44,10 +44,13 @@ public class OverriddenValueInspector extends XtextRuleInspector<Boolean, Parser
*/
private Set<AbstractRule> permanentlyVisited;
private Set<RuleCall> fragmentStack;
public OverriddenValueInspector(ValidationMessageAcceptor acceptor) {
super(acceptor);
assignedFeatures = newMultimap();
permanentlyVisited = Sets.newHashSet();
fragmentStack = Sets.newHashSet();
}
@Override
@ -102,20 +105,34 @@ public class OverriddenValueInspector extends XtextRuleInspector<Boolean, Parser
Collection<AbstractElement> sources = Lists.newArrayList(assignedFeatures.get(feature));
assignedFeatures.replaceValues(feature, Collections.<AbstractElement> emptyList());
if (sources != null && sources.equals(Collections.singletonList(object))) {
if (getNestingLevel() == 0)
acceptWarning("The assigned value of feature '" + feature
+ "' will possibly override itself because it is used inside of a loop.", object, null);
if (getNestingLevel() == 0 && fragmentStack.isEmpty()) {
if (object instanceof RuleCall) {
acceptWarning("The fragment will possibly override the assigned value of feature '" + feature
+ "' it is used inside of a loop.", object, null);
} else {
acceptWarning("The assigned value of feature '" + feature
+ "' will possibly override itself because it is used inside of a loop.", object, null);
}
}
}
else {
if (sources != null) {
if (getNestingLevel() == 0)
for (AbstractElement source : sources)
if (getNestingLevel() == 0 && fragmentStack.isEmpty()) {
for (AbstractElement source : sources) {
acceptWarning("The possibly assigned value of feature '" + feature
+ "' may be overridden by subsequent assignments.", source, null);
}
}
}
if (getNestingLevel() == 0 && fragmentStack.isEmpty()) {
if (object instanceof RuleCall) {
acceptWarning("The fragment will potentially override the possibly assigned value of feature '"
+ feature + "'.", object, null);
} else {
acceptWarning("This assignment will override the possibly assigned value of feature '"
+ feature + "'.", object, null);
}
}
if (getNestingLevel() == 0)
acceptWarning("This assignment will override the possibly assigned value of feature '"
+ feature + "'.", object, null);
}
}
else {
@ -131,6 +148,11 @@ public class OverriddenValueInspector extends XtextRuleInspector<Boolean, Parser
ParserRule parserRule = (ParserRule) calledRule;
if (GrammarUtil.isDatatypeRule(parserRule))
return Boolean.FALSE;
if (parserRule.isFragment()) {
visitFragment(object);
if (GrammarUtil.isMultipleCardinality(object))
visitFragment(object);
}
if (!addVisited(parserRule))
return Boolean.FALSE;
Multimap<String, AbstractElement> prevAssignedFeatures = assignedFeatures;
@ -143,6 +165,22 @@ public class OverriddenValueInspector extends XtextRuleInspector<Boolean, Parser
return Boolean.FALSE;
}
private void visitFragment(RuleCall object) {
Multimap<String, AbstractElement> prevAssignedFeatures = assignedFeatures;
assignedFeatures = newMultimap();
if (fragmentStack.add(object)) {
try {
doSwitch(object.getRule().getAlternatives());
} finally {
fragmentStack.remove(object);
}
}
Multimap<String, AbstractElement> assignedByFragment = assignedFeatures;
assignedFeatures = prevAssignedFeatures;
for (String feature : assignedByFragment.keySet())
checkAssignment(object, feature);
}
@Override
public Boolean caseAlternatives(Alternatives object) {
Multimap<String, AbstractElement> prevAssignedFeatures = assignedFeatures;

View file

@ -43,7 +43,7 @@ public class RuleWithoutInstantiationInspector extends XtextRuleInspector<Boolea
// special treatment of first rule
if (GrammarUtil.getGrammar(rule).getRules().get(0) == rule)
return false;
if (GrammarUtil.isDatatypeRule(rule) || rule.getAlternatives() == null)
if (GrammarUtil.isDatatypeRule(rule) || rule.getAlternatives() == null || rule.isFragment())
return false;
return super.canInspect(rule);
}

View file

@ -0,0 +1,201 @@
/*******************************************************************************
* Copyright (c) 2015 itemis AG (http://www.itemis.eu) and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*******************************************************************************/
package org.eclipse.xtext.xtext;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.xtext.AbstractRule;
import org.eclipse.xtext.Grammar;
import org.eclipse.xtext.GrammarUtil;
import org.eclipse.xtext.RuleCall;
import org.eclipse.xtext.naming.QualifiedName;
import org.eclipse.xtext.resource.EObjectDescription;
import org.eclipse.xtext.resource.ForwardingEObjectDescription;
import org.eclipse.xtext.resource.IEObjectDescription;
import org.eclipse.xtext.scoping.IScope;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
/**
* @author Sebastian Zarnekow - Initial contribution and API
*/
public class SuperCallScope implements IScope {
private static final String SUPER = "super";
/**
* Used during linking to set the {@link RuleCall#isExplicitlyCalled() flag}
* which keeps track of explicitly chosen rules.
* @author zarnekow - Initial contribution and API
*/
public static class ExplicitCallDescription extends ForwardingEObjectDescription {
private RuleCall ruleCall;
public ExplicitCallDescription(IEObjectDescription delegate, RuleCall ruleCall) {
super(delegate);
this.ruleCall = ruleCall;
}
@Override
public EObject getEObjectOrProxy() {
ruleCall.setExplicitlyCalled(true);
return super.getEObjectOrProxy();
}
}
private EObject context;
public SuperCallScope(EObject context) {
this.context = context;
}
@Override
public IEObjectDescription getSingleElement(QualifiedName name) {
IEObjectDescription result = doGetSingleElement(name);
if (result != null && context instanceof RuleCall) {
return new ExplicitCallDescription(result, (RuleCall) context);
}
return result;
}
private IEObjectDescription doGetSingleElement(QualifiedName qn) {
if (qn.getSegmentCount() == 1) {
if (SUPER.equals(qn.getFirstSegment())) {
AbstractRule containingRule = GrammarUtil.containingRule(context);
if (containingRule != null) {
Grammar grammar = GrammarUtil.getGrammar(containingRule);
for(Grammar parent: grammar.getUsedGrammars()) {
AbstractRule superRule = GrammarUtil.findRuleForName(parent, containingRule.getName());
if (superRule != null) {
return EObjectDescription.create(qn, superRule);
}
}
}
}
} else if (qn.getSegmentCount() > 1) {
Grammar grammar = GrammarUtil.getGrammar(context);
if (grammar != null) {
String firstSegment = qn.getFirstSegment();
if (qn.getSegmentCount() == 2) {
String ruleName = qn.getLastSegment();
if (SUPER.equals(firstSegment)) {
for(Grammar parent: grammar.getUsedGrammars()) {
AbstractRule superRule = GrammarUtil.findRuleForName(parent, ruleName);
if (superRule != null) {
return EObjectDescription.create(qn, superRule);
}
}
}
if (firstSegment.equals(GrammarUtil.getName(grammar))) {
AbstractRule rule = GrammarUtil.findRuleForName(grammar, grammar.getName() + "." + ruleName);
if (rule != null) {
return EObjectDescription.create(qn, rule);
}
}
for(Grammar usedGrammar: GrammarUtil.allUsedGrammars(grammar)) {
if (firstSegment.equals(GrammarUtil.getName(usedGrammar))) {
AbstractRule rule = GrammarUtil.findRuleForName(usedGrammar, usedGrammar.getName() + "." + ruleName);
if (rule != null) {
return EObjectDescription.create(qn, rule);
}
}
}
}
AbstractRule result = GrammarUtil.findRuleForName(grammar, qn.toString());
if (result != null) {
return EObjectDescription.create(qn, result);
}
}
}
return null;
}
@Override
public Iterable<IEObjectDescription> getElements(QualifiedName name) {
IEObjectDescription result = doGetSingleElement(name);
if (result != null) {
return Collections.singletonList(result);
}
return Collections.emptyList();
}
@Override
public IEObjectDescription getSingleElement(EObject object) {
return Iterables.getFirst(getElements(object), null);
}
@Override
public Iterable<IEObjectDescription> getElements(EObject object) {
if (object instanceof AbstractRule) {
Grammar grammar = GrammarUtil.getGrammar(context);
AbstractRule rule = (AbstractRule) object;
if (GrammarUtil.getGrammar(rule) == grammar) {
return Lists.newArrayList(
EObjectDescription.create(GrammarUtil.getName(grammar) + "." + rule.getName(), rule),
EObjectDescription.create(grammar.getName() + "." + rule.getName(), rule));
}
List<IEObjectDescription> result = Lists.newArrayList(
EObjectDescription.create(SUPER + "." + rule.getName(), rule),
EObjectDescription.create(GrammarUtil.getName(grammar) + "." + rule.getName(), rule),
EObjectDescription.create(grammar.getName() + "." + rule.getName(), rule));
AbstractRule contextRule = GrammarUtil.containingRule(context);
if (contextRule != null && contextRule.getName().equals(rule.getName())) {
result.add(0, EObjectDescription.create(SUPER, rule));
}
return result;
}
return Collections.emptyList();
}
@Override
public Iterable<IEObjectDescription> getAllElements() {
AbstractRule contextRule = GrammarUtil.containingRule(context);
Grammar grammar = contextRule != null ? GrammarUtil.getGrammar(contextRule) : GrammarUtil.getGrammar(context);
Map<QualifiedName, IEObjectDescription> result = Maps.newLinkedHashMap();
if (grammar != null) {
String shortName = GrammarUtil.getName(grammar) + ".";
String longName = grammar.getName() + ".";
for(AbstractRule rule: grammar.getRules()) {
putIfAbsent(EObjectDescription.create(shortName + rule.getName(), rule), result);
putIfAbsent(EObjectDescription.create(longName + rule.getName(), rule), result);
}
boolean waitingForSuper = contextRule != null;
for(Grammar usedGrammar: GrammarUtil.allUsedGrammars(grammar)) {
shortName = GrammarUtil.getName(usedGrammar) + ".";
longName = usedGrammar.getName() + ".";
for(AbstractRule rule: usedGrammar.getRules()) {
if (waitingForSuper) {
assert contextRule != null;
if (rule.getName().equals(contextRule.getName())) {
putIfAbsent(EObjectDescription.create(SUPER, rule), result);
waitingForSuper = false;
}
}
putIfAbsent(EObjectDescription.create(SUPER + "." + rule.getName(), rule), result);
putIfAbsent(EObjectDescription.create(shortName + rule.getName(), rule), result);
putIfAbsent(EObjectDescription.create(longName + rule.getName(), rule), result);
}
}
}
return Lists.newArrayList(result.values());
}
private void putIfAbsent(IEObjectDescription desc, Map<QualifiedName, IEObjectDescription> result) {
if (!result.containsKey(desc.getName())) {
result.put(desc.getName(), desc);
}
}
}

View file

@ -10,34 +10,36 @@ package org.eclipse.xtext.xtext;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EPackage;
import org.eclipse.emf.ecore.EReference;
import org.eclipse.xtext.AbstractMetamodelDeclaration;
import org.eclipse.xtext.AbstractRule;
import org.eclipse.xtext.Grammar;
import org.eclipse.xtext.RuleCall;
import org.eclipse.xtext.XtextPackage;
import org.eclipse.xtext.parsetree.reconstr.impl.CrossReferenceSerializer;
import org.eclipse.xtext.util.SimpleAttributeResolver;
/**
* @author Moritz Eysholdt - Initial contribution and API
*/
public class XtextCrossReferenceSerializer extends CrossReferenceSerializer {
private final SimpleAttributeResolver<EObject, String> aliasResolver;
public XtextCrossReferenceSerializer() {
super();
aliasResolver = SimpleAttributeResolver.newResolver(String.class, "alias");
}
@Override
protected String getUnconvertedLinkText(EObject object, EReference reference, EObject context) {
if (reference == XtextPackage.eINSTANCE.getGrammar_UsedGrammars())
return ((Grammar) object).getName();
if (reference == XtextPackage.eINSTANCE.getTypeRef_Metamodel())
return aliasResolver.getValue(object);
if (reference == XtextPackage.eINSTANCE.getTypeRef_Metamodel()) {
AbstractMetamodelDeclaration casted = (AbstractMetamodelDeclaration) object;
return casted.getAlias();
}
if (reference == XtextPackage.eINSTANCE.getAbstractMetamodelDeclaration_EPackage())
return ((EPackage) object).getNsURI();
if (object instanceof AbstractRule)
if (object instanceof AbstractRule) {
if (reference == XtextPackage.eINSTANCE.getRuleCall_Rule()) {
if (((RuleCall)context).isExplicitlyCalled()) {
return super.getUnconvertedLinkText(object, reference, context);
}
}
return ((AbstractRule) object).getName();
}
return super.getUnconvertedLinkText(object, reference, context);
}
}

View file

@ -18,8 +18,11 @@ import org.eclipse.xtext.services.XtextGrammarAccess.CharacterRangeElements;
import org.eclipse.xtext.services.XtextGrammarAccess.CrossReferenceElements;
import org.eclipse.xtext.services.XtextGrammarAccess.EnumLiteralDeclarationElements;
import org.eclipse.xtext.services.XtextGrammarAccess.GrammarElements;
import org.eclipse.xtext.services.XtextGrammarAccess.NamedArgumentElements;
import org.eclipse.xtext.services.XtextGrammarAccess.NegatedTokenElements;
import org.eclipse.xtext.services.XtextGrammarAccess.NegationElements;
import org.eclipse.xtext.services.XtextGrammarAccess.ParserRuleElements;
import org.eclipse.xtext.services.XtextGrammarAccess.RuleCallElements;
import org.eclipse.xtext.services.XtextGrammarAccess.TerminalTokenElements;
import org.eclipse.xtext.services.XtextGrammarAccess.TypeRefElements;
import org.eclipse.xtext.services.XtextGrammarAccess.UnorderedGroupElements;
@ -47,6 +50,14 @@ public class XtextFormatter extends AbstractDeclarativeFormatter {
cfg.setNoSpace().after(pair.getFirst());
cfg.setNoSpace().before(pair.getSecond());
}
for (Pair<Keyword, Keyword> pair : g.findKeywordPairs("[", "]")) {
cfg.setNoSpace().after(pair.getFirst());
cfg.setNoSpace().before(pair.getSecond());
}
for (Pair<Keyword, Keyword> pair : g.findKeywordPairs("<", ">")) {
cfg.setNoSpace().after(pair.getFirst());
cfg.setNoSpace().before(pair.getSecond());
}
for (Keyword comma : g.findKeywords(",")) {
cfg.setNoSpace().before(comma);
}
@ -72,7 +83,7 @@ public class XtextFormatter extends AbstractDeclarativeFormatter {
// ParserRule
ParserRuleElements pr = g.getParserRuleAccess();
cfg.setNoSpace().before(pr.getLeftParenthesisKeyword_2_1());
cfg.setNoSpace().before(pr.getLeftParenthesisKeyword_1_1());
// TypeRef
TypeRefElements typeRef = g.getTypeRefAccess();
@ -101,8 +112,6 @@ public class XtextFormatter extends AbstractDeclarativeFormatter {
// CrossReference
CrossReferenceElements cr = g.getCrossReferenceAccess();
cfg.setNoSpace().after(cr.getLeftSquareBracketKeyword_0());
cfg.setNoSpace().before(cr.getRightSquareBracketKeyword_3());
cfg.setNoSpace().around(cr.getVerticalLineKeyword_2_0());
// TerminalToken
@ -124,6 +133,20 @@ public class XtextFormatter extends AbstractDeclarativeFormatter {
// EnumLiteralDeclaration
EnumLiteralDeclarationElements eld = g.getEnumLiteralDeclarationAccess();
cfg.setNoSpace().around(eld.getEqualsSignKeyword_1_0());
// GuardCondition
NegationElements na = g.getNegationAccess();
cfg.setNoSpace().after(na.getExclamationMarkKeyword_1_1());
// RuleCall
RuleCallElements rca = g.getRuleCallAccess();
cfg.setNoSpace().before(rca.getLessThanSignKeyword_1_0());
// NamedArgument
NamedArgumentElements naa = g.getNamedArgumentAccess();
cfg.setNoSpace().around(naa.getCalledByNameAssignment_0_1());
cfg.setNoSpace().around(naa.getValueAssignment_1());
cfg.setNoSpace().around(naa.getParameterAssignment_0_0());
//saveDebugGraphvizDiagram("XtextFormatting.dot");
}

View file

@ -32,6 +32,7 @@ import org.eclipse.xtext.EnumRule;
import org.eclipse.xtext.GeneratedMetamodel;
import org.eclipse.xtext.Grammar;
import org.eclipse.xtext.GrammarUtil;
import org.eclipse.xtext.NamedArgument;
import org.eclipse.xtext.ParserRule;
import org.eclipse.xtext.ReferencedMetamodel;
import org.eclipse.xtext.RuleCall;
@ -40,10 +41,10 @@ import org.eclipse.xtext.XtextFactory;
import org.eclipse.xtext.XtextPackage;
import org.eclipse.xtext.diagnostics.AbstractDiagnosticProducerDecorator;
import org.eclipse.xtext.diagnostics.DiagnosticMessage;
import org.eclipse.xtext.diagnostics.Severity;
import org.eclipse.xtext.diagnostics.ExceptionDiagnostic;
import org.eclipse.xtext.diagnostics.IDiagnosticConsumer;
import org.eclipse.xtext.diagnostics.IDiagnosticProducer;
import org.eclipse.xtext.diagnostics.Severity;
import org.eclipse.xtext.linking.impl.Linker;
import org.eclipse.xtext.nodemodel.INode;
import org.eclipse.xtext.nodemodel.util.NodeModelUtils;
@ -132,7 +133,8 @@ public class XtextLinker extends Linker {
@Override
protected boolean canSetDefaultValues(EReference ref) {
return super.canSetDefaultValues(ref) || ref == XtextPackage.Literals.CROSS_REFERENCE__TERMINAL;
return super.canSetDefaultValues(ref)
|| ref == XtextPackage.Literals.CROSS_REFERENCE__TERMINAL;
}
@Override
@ -155,6 +157,20 @@ public class XtextLinker extends Linker {
call.setRule(rule);
((CrossReference) obj).setTerminal(call);
}
} else if (XtextPackage.eINSTANCE.getNamedArgument_Parameter() == ref) {
final NamedArgument argument = (NamedArgument) obj;
if (!argument.isCalledByName()) {
RuleCall ruleCall = EcoreUtil2.getContainerOfType(argument, RuleCall.class);
AbstractRule calledRule = ruleCall.getRule();
if (calledRule instanceof ParserRule && !calledRule.eIsProxy()) {
ParserRule casted = (ParserRule) calledRule;
int idx = ruleCall.getArguments().indexOf(argument);
if (idx < casted.getParameters().size()) {
argument.setParameter(casted.getParameters().get(idx));
return;
}
}
}
} else {
super.setDefaultValueImpl(obj, ref, producer);
}
@ -393,7 +409,7 @@ public class XtextLinker extends Linker {
}
final List<RuleCall> allRuleCalls = EcoreUtil2.getAllContentsOfType(grammar, RuleCall.class);
for (RuleCall call : allRuleCalls) {
if (call.getRule() != null) {
if (call.getRule() != null && !call.isExplicitlyCalled()) {
AbstractRule rule = rulePerName.get(call.getRule().getName());
if (rule != null)
call.setRule(rule);
@ -426,6 +442,9 @@ public class XtextLinker extends Linker {
if (node == null)
obj.eUnset(ref);
}
if (ref == XtextPackage.Literals.RULE_CALL__RULE) {
obj.eUnset(XtextPackage.Literals.RULE_CALL__EXPLICITLY_CALLED);
}
}
public void setPackageRemover(PackageRemover packageRemover) {

View file

@ -23,7 +23,7 @@ import com.google.common.collect.Sets;
/**
* @author Sebastian Zarnekow - Initial contribution and API
*/
public class XtextRuleInspector<Result, RuleType extends AbstractRule> extends XtextSwitch<Result> {
public abstract class XtextRuleInspector<Result, RuleType extends AbstractRule> extends XtextSwitch<Result> {
private final ValidationMessageAcceptor acceptor;

View file

@ -10,7 +10,6 @@ package org.eclipse.xtext.xtext;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.List;
import org.eclipse.emf.common.util.URI;
@ -27,9 +26,13 @@ import org.eclipse.emf.ecore.InternalEObject;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.emf.ecore.util.EcoreUtil;
import org.eclipse.xtext.AbstractMetamodelDeclaration;
import org.eclipse.xtext.AbstractRule;
import org.eclipse.xtext.EcoreUtil2;
import org.eclipse.xtext.EnumRule;
import org.eclipse.xtext.Grammar;
import org.eclipse.xtext.GrammarUtil;
import org.eclipse.xtext.ParserRule;
import org.eclipse.xtext.RuleCall;
import org.eclipse.xtext.TypeRef;
import org.eclipse.xtext.XtextPackage;
import org.eclipse.xtext.naming.QualifiedName;
@ -38,6 +41,7 @@ import org.eclipse.xtext.resource.IEObjectDescription;
import org.eclipse.xtext.resource.IResourceDescription;
import org.eclipse.xtext.scoping.IGlobalScopeProvider;
import org.eclipse.xtext.scoping.IScope;
import org.eclipse.xtext.scoping.Scopes;
import org.eclipse.xtext.scoping.impl.AbstractScopeProvider;
import org.eclipse.xtext.scoping.impl.SelectableBasedScope;
import org.eclipse.xtext.scoping.impl.SimpleScope;
@ -45,6 +49,7 @@ import org.eclipse.xtext.scoping.impl.SimpleScope;
import com.google.common.base.Function;
import com.google.common.base.Predicate;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.google.inject.Inject;
/**
@ -92,7 +97,28 @@ public class XtextScopeProvider extends AbstractScopeProvider {
}
});
}
return createScope(context.eResource(), reference.getEReferenceType());
if(reference == XtextPackage.eINSTANCE.getRuleCall_Rule()) {
return createScope(context.eResource(), reference.getEReferenceType(), new SuperCallScope(context));
}
if (reference == XtextPackage.eINSTANCE.getParameterReference_Parameter()) {
ParserRule rule = GrammarUtil.containingParserRule(context);
if (rule == null) {
return IScope.NULLSCOPE;
}
return Scopes.scopeFor(rule.getParameters());
}
if (reference == XtextPackage.eINSTANCE.getNamedArgument_Parameter()) {
RuleCall ruleCall = EcoreUtil2.getContainerOfType(context, RuleCall.class);
if (ruleCall == null) {
return IScope.NULLSCOPE;
}
AbstractRule referencedRule = ruleCall.getRule();
if (referencedRule instanceof ParserRule) {
return Scopes.scopeFor(((ParserRule) referencedRule).getParameters());
}
return IScope.NULLSCOPE;
}
return createScope(context.eResource(), reference.getEReferenceType(), IScope.NULLSCOPE);
}
protected IScope createEnumLiteralsScope(EEnum eEnum) {
@ -123,17 +149,17 @@ public class XtextScopeProvider extends AbstractScopeProvider {
return createClassifierScope(allClassifiers);
}
protected IScope createScope(Resource resource, EClass type) {
protected IScope createScope(Resource resource, EClass type, IScope parent) {
if (resource.getContents().size() < 1)
throw new IllegalArgumentException("resource is not as expected: contents.size == "
+ resource.getContents().size() + " but expected: >= 1");
final EObject firstContent = resource.getContents().get(0);
if (!(firstContent instanceof Grammar))
return IScope.NULLSCOPE;
return createScope((Grammar) firstContent, type);
return parent;
return createScope((Grammar) firstContent, type, parent);
}
protected IScope createScope(final Grammar grammar, EClass type) {
protected IScope createScope(final Grammar grammar, EClass type, IScope current) {
if (EcorePackage.Literals.EPACKAGE == type) {
return createEPackageScope(grammar);
} else if (AbstractMetamodelDeclaration.class.isAssignableFrom(type.getInstanceClass())) {
@ -147,30 +173,22 @@ public class XtextScopeProvider extends AbstractScopeProvider {
}));
}
final List<Grammar> allGrammars = getAllGrammars(grammar);
IScope current = IScope.NULLSCOPE;
for (int i = allGrammars.size() - 1; i >= 0; i--) {
current = createScope(allGrammars.get(i), type, current);
current = doCreateScope(allGrammars.get(i), type, current);
}
return current;
}
protected IScope createScope(final Grammar grammar, final EClass type, IScope parent) {
protected IScope doCreateScope(final Grammar grammar, final EClass type, IScope parent) {
final IResourceDescription resourceDescription = resourceDescriptionManager.getResourceDescription(grammar.eResource());
return SelectableBasedScope.createScope(parent, resourceDescription, type, false);
}
protected List<Grammar> getAllGrammars(Grammar grammar) {
Collection<Grammar> visitedGrammars = new LinkedHashSet<Grammar>();
collectAllUsedGrammars(grammar, visitedGrammars);
return new ArrayList<Grammar>(visitedGrammars);
}
protected void collectAllUsedGrammars(Grammar grammar, Collection<Grammar> visited) {
if (!visited.add(grammar))
return;
for(Grammar usedGrammar: grammar.getUsedGrammars()) {
collectAllUsedGrammars(usedGrammar, visited);
}
List<Grammar> result = Lists.newArrayList(grammar);
result.addAll(GrammarUtil.allUsedGrammars(grammar));
return result;
}
protected IScope createEPackageScope(final Grammar grammar, IScope parent) {

View file

@ -17,6 +17,7 @@ import org.eclipse.xtext.CrossReference;
import org.eclipse.xtext.EnumLiteralDeclaration;
import org.eclipse.xtext.EnumRule;
import org.eclipse.xtext.GrammarUtil;
import org.eclipse.xtext.NamedArgument;
import org.eclipse.xtext.ParserRule;
import org.eclipse.xtext.RuleCall;
import org.eclipse.xtext.TerminalRule;
@ -67,6 +68,12 @@ public class XtextTransientValueService extends DefaultTransientValueService {
return decl.getEnumLiteral() != null && decl.getLiteral() != null &&
Strings.equal(decl.getLiteral().getValue(), decl.getEnumLiteral().getName());
}
else if (feature == XtextPackage.eINSTANCE.getRuleCall_ExplicitlyCalled()) {
return true;
}
else if (feature == XtextPackage.eINSTANCE.getNamedArgument_Parameter()) {
return !((NamedArgument)owner).isCalledByName();
}
return super.isTransient(owner, feature, index);
}

View file

@ -19,6 +19,7 @@ import org.eclipse.xtext.CrossReference;
import org.eclipse.xtext.EnumLiteralDeclaration;
import org.eclipse.xtext.EnumRule;
import org.eclipse.xtext.GrammarUtil;
import org.eclipse.xtext.NamedArgument;
import org.eclipse.xtext.ParserRule;
import org.eclipse.xtext.RuleCall;
import org.eclipse.xtext.TerminalRule;
@ -80,6 +81,13 @@ public class XtextTransientValueService2 extends TransientValueService {
return NO;
}
return YES;
} else if (feature == XtextPackage.eINSTANCE.getRuleCall_ExplicitlyCalled()) {
return YES;
} else if (feature == XtextPackage.eINSTANCE.getNamedArgument_Parameter()) {
if (((NamedArgument)owner).isCalledByName()) {
return NO;
}
return YES;
}
return super.isValueTransient(owner, feature);
}

View file

@ -53,6 +53,8 @@ import org.eclipse.xtext.Grammar;
import org.eclipse.xtext.GrammarUtil;
import org.eclipse.xtext.Group;
import org.eclipse.xtext.Keyword;
import org.eclipse.xtext.NamedArgument;
import org.eclipse.xtext.Parameter;
import org.eclipse.xtext.ParserRule;
import org.eclipse.xtext.ReferencedMetamodel;
import org.eclipse.xtext.RuleCall;
@ -78,7 +80,6 @@ import org.eclipse.xtext.util.XtextSwitch;
import org.eclipse.xtext.validation.AbstractDeclarativeValidator;
import org.eclipse.xtext.validation.AbstractValidationMessageAcceptor;
import org.eclipse.xtext.validation.Check;
import org.eclipse.xtext.validation.CheckType;
import org.eclipse.xtext.validation.ValidationMessageAcceptor;
import org.eclipse.xtext.xtext.ecoreInference.SourceAdapter;
@ -109,8 +110,74 @@ public class XtextValidator extends AbstractDeclarativeValidator {
protected List<EPackage> getEPackages() {
return Collections.<EPackage>singletonList(XtextPackage.eINSTANCE);
}
@Check
public void checkOrderOfArguments(RuleCall call) {
AbstractRule rule = call.getRule();
if (rule instanceof ParserRule) {
Set<Parameter> usedParameters = Sets.newHashSet();
boolean hasError = false;
boolean hasPositionalArgument = false;
boolean hasNamedArgument = false;
for(NamedArgument argument: call.getArguments()) {
Parameter parameter = argument.getParameter();
if (parameter == null || parameter.eIsProxy()) {
hasError = true;
} else if (!usedParameters.add(parameter)) {
hasError = true;
error("Duplicate value for parameter " + parameter.getName(),
argument, XtextPackage.Literals.NAMED_ARGUMENT__PARAMETER);
}
if (!argument.isCalledByName()) {
hasPositionalArgument = true;
} else {
hasNamedArgument = true;
}
}
if (hasError) {
return;
}
List<Parameter> parameters = ((ParserRule) rule).getParameters();
if (!hasPositionalArgument) {
if (usedParameters.size() != parameters.size()) {
StringBuilder missing = new StringBuilder();
int count = 0;
for(Parameter parameter: parameters) {
if (!usedParameters.contains(parameter)) {
if (count > 0) {
missing.append(", ");
}
missing.append(parameter.getName());
count++;
}
}
if (count == 1) {
error("Missing argument for parameter " + missing,
call, XtextPackage.Literals.RULE_CALL__RULE);
} else {
error(count + " missing arguments for the following parameters: " + missing,
call, XtextPackage.Literals.RULE_CALL__RULE);
}
}
} else {
if (usedParameters.size() != parameters.size()) {
error(String.format("Expected %d arguments but got %d", parameters.size(), usedParameters.size()),
call, XtextPackage.Literals.RULE_CALL__RULE);
} else if (hasNamedArgument) {
for(int i = 0, max = Math.min(usedParameters.size(), parameters.size()); i < max; i++) {
NamedArgument argument = call.getArguments().get(i);
Parameter param = parameters.get(i);
if (argument.isCalledByName() && argument.getParameter() != param) {
error("Out of sequence named argument. Expected value for " + param.getName(),
argument, XtextPackage.Literals.NAMED_ARGUMENT__PARAMETER);
}
}
}
}
}
}
@Check(CheckType.FAST)
@Check
public void checkGrammarUsesMaxOneOther(Grammar grammar) {
if (grammar.getUsedGrammars().size() > 1) {
for(int i = 1; i < grammar.getUsedGrammars().size(); i++) {
@ -122,7 +189,7 @@ public class XtextValidator extends AbstractDeclarativeValidator {
}
}
@Check(CheckType.FAST)
@Check
public void checkGrammarRecursiveReference(Grammar grammar) {
Set<Grammar> visitedGrammars = Sets.newHashSet(grammar);
for (int i = 0; i < grammar.getUsedGrammars().size(); i++) {
@ -684,7 +751,7 @@ public class XtextValidator extends AbstractDeclarativeValidator {
@Check
public void checkUnassignedRuleCallAllowed(final RuleCall call) {
if (call.getRule() != null && !call.getRule().eIsProxy() && GrammarUtil.containingAssignment(call) == null) {
AbstractRule container = EcoreUtil2.getContainerOfType(call, AbstractRule.class);
AbstractRule container = GrammarUtil.containingRule(call);
if (call.getRule() instanceof ParserRule) {
if (container instanceof TerminalRule) {
getMessageAcceptor().acceptError(
@ -693,9 +760,12 @@ public class XtextValidator extends AbstractDeclarativeValidator {
XtextPackage.Literals.RULE_CALL__RULE,
ValidationMessageAcceptor.INSIGNIFICANT_INDEX,
null);
} else {
ParserRule parserRule = (ParserRule) call.getRule();
if (!GrammarUtil.isDatatypeRule(parserRule) && !parserRule.isFragment()) {
checkCurrentMustBeUnassigned(call);
}
}
else if (!GrammarUtil.isDatatypeRule((ParserRule) call.getRule()))
checkCurrentMustBeUnassigned(call);
}
if (call.getRule() instanceof EnumRule) {
if (container instanceof TerminalRule) {
@ -714,7 +784,7 @@ public class XtextValidator extends AbstractDeclarativeValidator {
public void checkTerminalFragmentCalledFromTerminalRule(final RuleCall call) {
if (call.getRule() != null && !call.getRule().eIsProxy()) {
if (call.getRule() instanceof TerminalRule && ((TerminalRule) call.getRule()).isFragment()) {
AbstractRule container = EcoreUtil2.getContainerOfType(call, AbstractRule.class);
AbstractRule container = GrammarUtil.containingRule(call);
if (!(container instanceof TerminalRule)) {
getMessageAcceptor().acceptError(
"Only terminal rules may use terminal fragments.",
@ -728,12 +798,11 @@ public class XtextValidator extends AbstractDeclarativeValidator {
}
private void checkCurrentMustBeUnassigned(final AbstractElement element) {
ParserRule rule = GrammarUtil.containingParserRule(element);
final ParserRule rule = GrammarUtil.containingParserRule(element);
if (GrammarUtil.isDatatypeRule(rule))
return;
XtextSwitch<Boolean> visitor = new XtextSwitch<Boolean>() {
private boolean isNull = true;
private boolean isNull = !rule.isFragment();
@Override
public Boolean caseAbstractElement(AbstractElement object) {
@ -781,7 +850,7 @@ public class XtextValidator extends AbstractDeclarativeValidator {
public Boolean caseAction(Action object) {
if (object == element) {
if (!(isNull && !isMany(object))) {
error("An unassigned action is not allowed, when the 'current' was already created.", null);
error("An unassigned action is not allowed, when the 'current' was already created.", object, null);
checkDone();
}
}
@ -792,8 +861,13 @@ public class XtextValidator extends AbstractDeclarativeValidator {
@Override
public Boolean caseRuleCall(RuleCall object) {
if (object == element) {
AbstractRule calledRule = object.getRule();
if (calledRule instanceof ParserRule && ((ParserRule) calledRule).isFragment()) {
isNull = false;
return isNull;
}
if (!(isNull && !isMany(object))) {
error("An unassigned rule call is not allowed, when the 'current' was already created.", null);
error("An unassigned rule call is not allowed, when the 'current' was already created.", object, null);
checkDone();
}
}
@ -826,6 +900,9 @@ public class XtextValidator extends AbstractDeclarativeValidator {
public void checkAssignedActionAfterAssignment(final Action action) {
if (action.getFeature() != null) {
ParserRule rule = GrammarUtil.containingParserRule(action);
if (rule.isFragment() && !rule.isWildcard()) {
return;
}
XtextSwitch<Boolean> visitor = new XtextSwitch<Boolean>() {
private boolean assignedActionAllowed = false;
@ -880,7 +957,7 @@ public class XtextValidator extends AbstractDeclarativeValidator {
public Boolean caseAction(Action object) {
if (object == action) {
if (!assignedActionAllowed) {
error("An action is not allowed, when the current may still be unassigned.", null);
error("An action is not allowed in wildcard fragments and when the current may still be unassigned.", null);
checkDone();
}
}
@ -899,7 +976,7 @@ public class XtextValidator extends AbstractDeclarativeValidator {
@Override
public Boolean caseParserRule(ParserRule object) {
assignedActionAllowed = !GrammarUtil.isDatatypeRule(object);
assignedActionAllowed = !GrammarUtil.isDatatypeRule(object) && !(object.isFragment() && object.isWildcard());
return assignedActionAllowed;
}

View file

@ -13,14 +13,28 @@ import org.eclipse.xtext.conversion.IValueConverter;
import org.eclipse.xtext.conversion.ValueConverter;
import org.eclipse.xtext.conversion.ValueConverterException;
import org.eclipse.xtext.conversion.impl.AbstractNullSafeConverter;
import org.eclipse.xtext.conversion.impl.KeywordAlternativeConverter;
import org.eclipse.xtext.nodemodel.ILeafNode;
import org.eclipse.xtext.nodemodel.INode;
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.Singleton;
/**
* @author Sebastian Zarnekow - Initial contribution and API
*/
@Singleton
public class XtextValueConverters extends DefaultTerminalConverters {
@Inject
private Provider<KeywordAlternativeConverter> validIDConverter;
@ValueConverter(rule = "ValidID")
public IValueConverter<String> ValidID() {
return validIDConverter.get();
}
@ValueConverter(rule = "GrammarID")
public IValueConverter<String> GrammarID() {
return new AbstractNullSafeConverter<String>() {
@ -51,5 +65,51 @@ public class XtextValueConverters extends DefaultTerminalConverters {
}
};
}
@ValueConverter(rule = "RuleID")
public IValueConverter<String> RuleID() {
return new AbstractNullSafeConverter<String>() {
@Override
protected String internalToValue(String string, INode node) throws ValueConverterException {
StringBuilder result = new StringBuilder();
for(ILeafNode leaf: node.getLeafNodes()) {
if (!leaf.isHidden()) {
if (leaf.getGrammarElement() instanceof Keyword)
result.append(".");
else
result.append(ID().toValue(leaf.getText(), leaf));
}
}
return result.toString();
}
@Override
protected String internalToString(String value) {
String[] splitted = value.split("\\.");
StringBuilder result = new StringBuilder(value.length());
for(int i = 0; i < splitted.length; i++) {
if (i != 0)
result.append("::");
result.append(ID().toString(splitted[i]));
}
return result.toString();
}
};
}
@ValueConverter(rule = "LiteralValue")
public IValueConverter<Boolean> LiteralValue() {
return new AbstractNullSafeConverter<Boolean>() {
@Override
protected Boolean internalToValue(String string, INode node) throws ValueConverterException {
return "+".equals(string);
}
@Override
protected String internalToString(Boolean value) {
return value.booleanValue() ? "+" : "!";
}
};
}
}

View file

@ -21,7 +21,9 @@ public enum TransformationErrorCode {
UnknownMetaModelAlias,
InvalidDatatypeRule,
InvalidSupertype,
InvalidFeature;
InvalidFeature,
InvalidFragmentOverride,
InvalidRuleOverride;
public String getFullyQualifiedCode() {
return getClass().getCanonicalName() + "." + name();

View file

@ -19,7 +19,6 @@ import java.util.Map;
import java.util.Set;
import org.apache.log4j.Logger;
import org.eclipse.emf.common.util.EList;
import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.ecore.EClass;
import org.eclipse.emf.ecore.EClassifier;
@ -204,31 +203,33 @@ public class Xtext2EcoreTransformer {
for (AbstractRule rule : grammar.getRules()) {
try {
EClassifierInfo generatedEClass = findOrCreateEClassifierInfo(rule);
if (rule instanceof ParserRule) {
ParserRule parserRule = (ParserRule) rule;
if (parserRule.getAlternatives() != null) {
if (!GrammarUtil.isDatatypeRule(parserRule)) {
deriveTypesAndHierarchy(parserRule, generatedEClass, parserRule.getAlternatives());
} else {
if (generatedEClass != null || !isWildcardFragment(rule)) {
if (rule instanceof ParserRule) {
ParserRule parserRule = (ParserRule) rule;
if (parserRule.getAlternatives() != null) {
if (!GrammarUtil.isDatatypeRule(parserRule)) {
deriveTypesAndHierarchy(parserRule, generatedEClass, parserRule.getAlternatives());
} else {
checkSupertypeOfOverriddenDatatypeRule(rule);
}
}
} else if (rule instanceof TerminalRule) {
if (rule.getType() != null) {
if (!(rule.getType().getClassifier() instanceof EDataType))
throw new TransformationException(TransformationErrorCode.NoSuchTypeAvailable,
"Return type of a terminal rule must be an EDataType.", rule.getType());
checkSupertypeOfOverriddenDatatypeRule(rule);
}
} else if (rule instanceof EnumRule) {
if (rule.getType() != null) {
if (!(rule.getType().getClassifier() instanceof EEnum))
throw new TransformationException(TransformationErrorCode.NoSuchTypeAvailable,
"Return type of an enum rule must be an EEnum.", rule.getType());
checkSupertypeOfOverriddenDatatypeRule(rule);
}
} else {
throw new IllegalStateException("Unknown rule type: " + rule.eClass().getName());
}
} else if (rule instanceof TerminalRule) {
if (rule.getType() != null) {
if (!(rule.getType().getClassifier() instanceof EDataType))
throw new TransformationException(TransformationErrorCode.NoSuchTypeAvailable,
"Return type of a terminal rule must be an EDataType.", rule.getType());
checkSupertypeOfOverriddenDatatypeRule(rule);
}
} else if (rule instanceof EnumRule) {
if (rule.getType() != null) {
if (!(rule.getType().getClassifier() instanceof EEnum))
throw new TransformationException(TransformationErrorCode.NoSuchTypeAvailable,
"Return type of an enum rule must be an EEnum.", rule.getType());
checkSupertypeOfOverriddenDatatypeRule(rule);
}
} else {
throw new IllegalStateException("Unknown rule type: " + rule.eClass().getName());
}
}
catch (TransformationException e) {
@ -320,7 +321,7 @@ public class Xtext2EcoreTransformer {
boolean result = true;
for (AbstractRule rule : grammar.getRules()) {
try {
if (rule instanceof ParserRule && !GrammarUtil.isDatatypeRule((ParserRule) rule)) {
if (rule instanceof ParserRule && !GrammarUtil.isDatatypeRule((ParserRule) rule) && !isWildcardFragment(rule)) {
deriveFeatures((ParserRule) rule);
} else if (rule instanceof EnumRule) {
deriveEnums((EnumRule) rule);
@ -420,11 +421,15 @@ public class Xtext2EcoreTransformer {
@Override
public Xtext2EcoreInterpretationContext caseGroup(Group object) {
Xtext2EcoreInterpretationContext result = deriveFeatures(context.spawnContextForGroup(), object.getElements());
if (GrammarUtil.isMultipleCardinality(object)) {
result = deriveFeatures(result.spawnContextForGroup(), object.getElements());
return visitElements(object, object.getElements());
}
private Xtext2EcoreInterpretationContext visitElements(AbstractElement caller, List<AbstractElement> elementsToProcess) {
Xtext2EcoreInterpretationContext result = deriveFeatures(context.spawnContextForGroup(), elementsToProcess);
if (GrammarUtil.isMultipleCardinality(caller)) {
result = deriveFeatures(result.spawnContextForGroup(), elementsToProcess);
}
if (GrammarUtil.isOptionalCardinality(object)) {
if (GrammarUtil.isOptionalCardinality(caller)) {
result = result.mergeSpawnedContexts(Arrays.asList(context, result));
}
return result;
@ -450,8 +455,14 @@ public class Xtext2EcoreTransformer {
@Override
public Xtext2EcoreInterpretationContext caseRuleCall(RuleCall object) {
AbstractRule calledRule = object.getRule();
if (isWildcardFragment(calledRule)) {
return visitElements(object, Collections.singletonList(calledRule.getAlternatives()));
}
if (isParserRuleFragment(calledRule)) {
return context;
}
if (!GrammarUtil.isOptionalCardinality(object)) {
AbstractRule calledRule = object.getRule();
// do not throw an exception for missing rules, these have been
// announced during the first iteration
if (calledRule != null && calledRule instanceof ParserRule && !GrammarUtil.isDatatypeRule((ParserRule) calledRule)) {
@ -495,7 +506,7 @@ public class Xtext2EcoreTransformer {
}
private Xtext2EcoreInterpretationContext deriveFeatures(Xtext2EcoreInterpretationContext context,
EList<AbstractElement> elements) {
List<AbstractElement> elements) {
Xtext2EcoreInterpretationContext result = context;
for (AbstractElement element : elements) {
result = deriveFeatures(result, element);
@ -605,19 +616,14 @@ public class Xtext2EcoreTransformer {
final TypeHierarchyHelper helper = new TypeHierarchyHelper(grammar, this.eClassifierInfos, this.errorAcceptor);
helper.liftUpFeaturesRecursively();
helper.removeDuplicateDerivedFeatures();
// helper.detectEClassesWithCyclesInTypeHierachy();
// duplicated features can occur in rare cases when alternatives produce
// different types of a feature
// If the internal structure (Set) of the underlying algorithm
// produces the features for the subtype first the implementation of EClassInfo
// wont find a conflict
// helper.detectDuplicatedFeatures();
}
private void deriveTypesAndHierarchy(final ParserRule rule, final EClassifierInfo ruleReturnType,
AbstractElement element) throws TransformationException {
TransformationException ex = new XtextSwitch<TransformationException>() {
Set<AbstractRule> visiting = Sets.newHashSet();
@Override
public TransformationException caseAction(Action action) {
final TypeRef actionTypeRef = action.getType();
@ -633,10 +639,8 @@ public class Xtext2EcoreTransformer {
@Override
public TransformationException caseCompoundElement(CompoundElement object) {
for (AbstractElement ele : object.getElements()) {
try {
deriveTypesAndHierarchy(rule, ruleReturnType, ele);
}
catch (TransformationException ex) {
TransformationException ex = doSwitch(ele);
if (ex != null) {
return ex;
}
}
@ -656,21 +660,40 @@ public class Xtext2EcoreTransformer {
"Cannot find called rule.", ruleCall);
}
if (calledRule instanceof TerminalRule ||
calledRule instanceof ParserRule && (GrammarUtil.isDatatypeRule((ParserRule) calledRule)))
calledRule instanceof ParserRule && (GrammarUtil.isDatatypeRule((ParserRule) calledRule))
|| isWildcardFragment(calledRule))
return null;
if (calledRule instanceof EnumRule) {
return new TransformationException(TransformationErrorCode.NoSuchRuleAvailable,
"Cannot call enum rule without assignment.", ruleCall);
}
final TypeRef calledRuleReturnTypeRef = getOrComputeReturnType(calledRule);
try {
addSuperType(rule, calledRuleReturnTypeRef, ruleReturnType);
}
catch (TransformationException ex) {
if (isParserRuleFragment(calledRule)) {
TypeRef subTypeRef = getOrComputeReturnType(rule);
addSuperType(rule, subTypeRef, findOrCreateEClassifierInfo(calledRule));
if (visiting.add(calledRule)) {
try {
AbstractElement fragment = calledRule.getAlternatives();
doSwitch(fragment);
} finally {
visiting.remove(calledRule);
}
}
} else {
final TypeRef calledRuleReturnTypeRef = getOrComputeReturnType(calledRule);
addSuperType(rule, calledRuleReturnTypeRef, ruleReturnType);
}
} catch (TransformationException ex) {
return ex;
}
return null;
}
@Override
public TransformationException defaultCase(EObject object) {
// ignore
return null;
}
}.doSwitch(element);
if (ex != null)
@ -684,20 +707,80 @@ public class Xtext2EcoreTransformer {
private boolean deriveTypeHierarchyFromOverridden(ParserRule rule, Grammar grammar) throws TransformationException {
AbstractRule parentRule = GrammarUtil.findRuleForName(grammar, rule.getName());
if (parentRule != null && parentRule.getType() != null && parentRule != rule) {
if (parentRule.getType().getClassifier() instanceof EDataType)
throw new TransformationException(TransformationErrorCode.InvalidSupertype,
"Cannot inherit from datatype rule and return another type.", rule.getType());
EClassifierInfo parentTypeInfo = eClassifierInfos.getInfoOrNull(parentRule.getType());
if (parentTypeInfo == null)
throw new TransformationException(TransformationErrorCode.InvalidSupertype,
"Cannot determine return type of overridden rule.", rule.getType());
addSuperType(rule, rule.getType(), parentTypeInfo);
return true;
if (parentRule != null) {
if (parentRule != rule && parentRule instanceof ParserRule) {
ParserRule casted = (ParserRule) parentRule;
if (casted.isFragment() != rule.isFragment()) {
if (rule.isFragment()) {
throw new TransformationException(TransformationErrorCode.InvalidFragmentOverride,
"A fragment rule cannot override a production rule.", rule);
} else {
throw new TransformationException(TransformationErrorCode.InvalidFragmentOverride,
"Only fragment rule can override other fragment rules.", rule);
}
}
if (casted.isWildcard() != rule.isWildcard()) {
if (rule.isWildcard()) {
throw new TransformationException(TransformationErrorCode.InvalidFragmentOverride,
"A wildcard fragment rule cannot override a typed fragment rule.", rule);
} else {
throw new TransformationException(TransformationErrorCode.InvalidFragmentOverride,
"Only wildcard fragment rules can override other wildcard fragments.", rule);
}
}
if (rule.isFragment() && !rule.isWildcard() && parentRule.getType() != null) {
if (rule.getType().getClassifier() != parentRule.getType().getClassifier()) {
throw new TransformationException(TransformationErrorCode.InvalidFragmentOverride,
"Overriding fragment rules cannot redeclare their type.", rule.getType());
}
}
checkParameterLists(rule, casted);
}
if (parentRule.getType() != null && parentRule != rule) {
if (parentRule.getType().getClassifier() instanceof EDataType)
throw new TransformationException(TransformationErrorCode.InvalidSupertype,
"Cannot inherit from datatype rule and return another type.", rule.getType());
EClassifierInfo parentTypeInfo = eClassifierInfos.getInfoOrNull(parentRule.getType());
if (parentTypeInfo == null)
throw new TransformationException(TransformationErrorCode.InvalidSupertype,
"Cannot determine return type of overridden rule.", rule.getType());
addSuperType(rule, rule.getType(), parentTypeInfo);
return true;
}
}
return false;
}
private void checkParameterLists(ParserRule rule, ParserRule overridden) throws TransformationException {
int inherited = overridden.getParameters().size();
if (inherited == rule.getParameters().size()) {
boolean ok = true;
for(int i = 0; ok && i < inherited; i++) {
if (!Strings.equal(rule.getParameters().get(i).getName(), overridden.getParameters().get(i).getName())) {
ok = false;
}
}
if (ok) {
return;
}
}
if (inherited == 0) {
throw new TransformationException(TransformationErrorCode.InvalidRuleOverride,
"Overridden rule " + rule.getName() + " does not declare any parameters", rule);
}
StringBuilder message = new StringBuilder("Parameter list is incompatible with inherited ");
message.append(rule.getName()).append("[");
for(int i = 0; i < overridden.getParameters().size(); i++) {
if (i != 0) {
message.append(", ");
}
message.append(overridden.getParameters().get(i).getName());
}
message.append("]");
throw new TransformationException(TransformationErrorCode.InvalidRuleOverride,
message.toString(), rule);
}
private void addSuperType(ParserRule rule, TypeRef subTypeRef, EClassifierInfo superTypeInfo) throws TransformationException {
final EClassifier subType = subTypeRef.getClassifier();
final EClassifierInfo subTypeInfo = subType == null
@ -841,14 +924,34 @@ public class Xtext2EcoreTransformer {
}
private EClassifierInfo findOrCreateEClassifierInfo(AbstractRule rule) throws TransformationException {
if (isWildcardFragment(rule)) {
return null;
}
final TypeRef typeRef = getOrComputeReturnType(rule);
if (typeRef == null)
if (typeRef == null) {
throw new TransformationException(TransformationErrorCode.NoSuchTypeAvailable, "Cannot create type for unnamed rule.", rule);
}
if (typeRef.getMetamodel() != null && typeRef.getMetamodel().getEPackage() == null)
throw new TransformationException(TransformationErrorCode.UnknownMetaModelAlias, "Cannot create type without declared package.", typeRef);
return findOrCreateEClassifierInfo(typeRef, rule.getName(), grammar.getRules().contains(rule));
}
private boolean isWildcardFragment(AbstractRule rule) {
if (rule instanceof ParserRule) {
ParserRule casted = (ParserRule) rule;
return casted.isFragment() && casted.isWildcard();
}
return false;
}
private boolean isParserRuleFragment(AbstractRule rule) {
if (rule instanceof ParserRule) {
ParserRule casted = (ParserRule) rule;
return casted.isFragment() && !casted.isWildcard();
}
return false;
}
private EClassifierInfo findEClassifierInfo(AbstractRule rule) {
final TypeRef typeRef = getOrComputeReturnType(rule);
if (typeRef == null)
@ -884,8 +987,7 @@ public class Xtext2EcoreTransformer {
+ typeRef.getClassifier().getName());
// + GrammarUtil.getQualifiedName(typeRef));
String classifierName = null;
classifierName = GrammarUtil.getTypeRefName(typeRef);
String classifierName = GrammarUtil.getTypeRefName(typeRef);
if (classifierName == null)
classifierName = name;
if (classifierName == null)