Cross-linking to alternative representations (not always having the 'name' attribute)? [message #670266] |
Fri, 13 May 2011 10:19 |
Pavol Mederly Messages: 3 Registered: May 2011 |
Junior Member |
|
|
Hello,
in my language I would like to declare variables in two ways - using an explicit variable name (i.e. "variable <type> <name>") or using implicit name, derived from the type name (i.e. just "variable <type>").
Then I would like to link to both kinds of these definitions, i.e. having "ref <variable-name>".
E.g.
type Type1;
variable Type1 var1; // explicit form
variable Type1; // implicit form (it should declare variable 'type1')
ref var1; // this works
ref type1; // this is desired but does not work
The grammar currently looks like this:
All:
(variables+=Variable | types+=Type | references+=Reference)*;
Type: 'type' name=ID ';';
Variable:
'variable' type=[Type] name=ID? ';' ;
Reference:
'ref' item=[Variable] ';' ;
What is the best way to achieve this goal? (I would like to have the "Ctrl-Space"
functionality as well.) Also, it should be usable with namespaces - after trying hardly (and unsuccessfully) in that context, however, I have prepared this simple example to show the essence of the problem.
I know that I have to initialize the 'name' attribute of the implicit variable declarations somehow, but I do not know how. Or I have to customize IQualifiedNameProvider to return a name derived from the name of type (in the 'implicit' version). However, I am not able to determine the type name at the runtime (in the IQualifiedNameProvider), as I always get "Cyclic resolution of lazy links" errors.
Please, how to best achieve that goal?
Thank you in advance.
Pavol
PS: here is the code for the modified IQualifiedNameProvider, taken out from the original context:
public String getQualifiedName(final EObject obj)
{
return cache.get(Tuples.pair(obj, "fqn"), obj.eResource(), new Provider<String>()
{
public String get()
{
EObject temp = obj;
String value;
if (temp instanceof Variable)
{
Variable v = (Variable) temp;
value = v.getName();
if (value != null)
return value;
value = v.getType().getName(); // here it cycles!
if (value == null || "".equals(value))
return null;
else
return Character.toLowerCase(value.charAt(0)) + value.substring(1);
}
String name = qualifiedName.invoke(temp);
if (name != null)
return name;
value = resolver.getValue(temp);
if (value == null)
return null;
while (temp.eContainer() != null) {
temp = temp.eContainer();
String parentsName = getQualifiedName(temp);
if (parentsName != null)
return parentsName + getDelimiter() + value;
}
return value;
}
});
|
|
|
|
Re: Cross-linking to alternative representations (not always having the 'name' attribute)? [message #670792 is a reply to message #670514] |
Sun, 15 May 2011 15:16 |
Pavol Mederly Messages: 3 Registered: May 2011 |
Junior Member |
|
|
Hello Alex,
thank you for the reply. I've tried it, creating own QNameProvider as you have suggested:
public String qualifiedName(Variable var)
{
if (var.getName() != null)
return var.getName();
else
return var.getType().getName();
}
However, I get the same errors are before: An internal error occurred during: "Xtext validation". Cyclic resolution of lazy links : Variable.type->Variable.type.
After some experiments it seems that the problem is in that the variable type is a reference (not a simple terminal):
Variable:
'variable' type=[Type] name=ID? ';' ;
The "type" ecore feature is then a proxy. The VariableImpl.getType() method fails when trying to resolve EObject proxy for "type". More specifically, when resolving "type" proxy it seems that the framework tries to determine QNames for (all?) entities in the source file, our variable element being among them (there comes the cycle - it calls again the getType() and eResolveProxy(type) within it).
Out of total despair I tried to modify ANTLR grammar description, changing a type-matching part of the rule for Variable like this:
typename=RULE_ID
{
// original code
createLeafNode(grammarAccess.getVariableAccess().getTypeTypeCrossReference_1_0(), "type");
// added code
System.out.println("Setting type name: " + typename);
try {
set(
$current,
"name",
typename,
"ID",
lastConsumedNode);
} catch (ValueConverterException vce) {
handleValueConverterException(vce);
}
}
Being a novice in Xtext/Ecore/ANTLR it is a pure guess, unfortunately Actually, it seems to work, but such a modification of a generated code is obviously rather unclean and I'm not sure if it has not any unexpected negative consequences (i.e. will it really work?).
Is there a cleaner way? Perhaps by using some more sophisticated action in Xtext grammar? (BTW, what are allowed kinds of Xtext actions? In the documentation I have only saw {Class} and {Class.feature=current} ones...)
Thank you in advance!
Pavol
|
|
|
Re: Cross-linking to alternative representations (not always having the 'name' attribute)? [message #670823 is a reply to message #670792] |
Sun, 15 May 2011 18:37 |
Alexander Nittka Messages: 1193 Registered: July 2009 |
Senior Member |
|
|
Hi,
currently, I have no idea for a clean solution. The main problem is that the name calculation is done before the linking (of course, as the linking depends on the names of elements). Hence resolving cross references (as opposed to containment references) during the name calculation is a bit of a problem.
You can of course bind your own parser (it is not necessary to edit the generated sources). Extend the generated one only overriding the problematic method and bind your implementation in the runtime module. But in your case, I would rather "hack" the qualified name calculation.
If your sample is not simplified too much, something like the following might be good enough for you.
String qualifiedName(Variable var){
if (var.getName() != null){
return var.getName();
}
else{
//go to the parse tree and extract the actual string used for referencing
//note that you have to substitute MyDslPackage with the package of your language
List<AbstractNode> typeNodes=NodeUtil.findNodesForFeature(var, MyDslPackage.eINSTANCE.getVariable_Type());
//brute force concatenation, please actually test that the terminal rule behind the abstract node (LeafNode)
//is not a comment, otherwise the comment is added to the name, same is true for white spaces
String name="";
for (AbstractNode abstractNode : typeNodes) {
name+=abstractNode.serialize();
}
//trim is just a quick and dirty workaround
return name.trim().toLowerCase();
}
}
Alex
P.S.: please note that this is really a quick and dirty workaround
|
|
|
|
Powered by
FUDForum. Page generated in 0.03326 seconds