Content proposal for grammar with syntactically similar rules [message #1793858] |
Fri, 17 August 2018 11:34 |
Daniel Darvas Messages: 10 Registered: July 2014 |
Junior Member |
|
|
Hello,
TL;DR: having trouble providing content proposals with a grammar where two rules are syntactically very similar and potentially indistinguishable at the moment of providing the proposals.
I'm trying to provide content assist for a language in Xtext (v2.12). Here is a grammar similar to the original one, which is focusing on the current issue:
grammar org.xtext.example.mydsl.MyDsl with org.eclipse.xtext.common.Terminals
generate myDsl "http://www.xtext.org/example/mydsl/MyDsl"
Model:
(declarations+=Declaration)+
statement=Statement;
Namespace:
'namespace' name=ID '{'
(declarations+=Declaration)*
'}';
Declaration: Namespace | Variable;
Variable:
'variable' name=ID;
Statement:
ReferenceStatement /*| ConflictingStatement*/;
ReferenceStatement:
'stmt' ref=Reference;
/* ConflictingStatement:
'stmt' name1=[Variable] '.' name2=[Namespace] '(' ')'; */
Reference:
QualifiedReference;
QualifiedReference returns Reference:
DirectReference ({QualifiedReference.prefix=current} '.' ref=DirectReference)*;
DirectReference:
ref=[Declaration];
Here is an example instance:
namespace a {
namespace b {
variable c
variable d
}
}
variable x
stmt a.b.c
If the 'ConflictingStatement' rule is not used (commented out as above), everything is fine. I have written custom scoping for the qualified references. With this, the hierarchical references such as 'a.b.c' do work as expected. In the 'MyDslProposalProvider', I have overridden the 'completeQualifiedReference_Ref' method, and this is called with a 'QualifiedReference' instance as model when a proposal is required after 'a.' or 'a.b.'. This way I can access the prefix and provide correct suggestions. This is all as expected and needed.
However, I need the 'ConflictingStatement' rule in my grammar too. (Please discard the fact for a moment that the current 'ConflictingStatement' doesn't make any sense in any reasonable language.) If I include this rule too in the grammar, the 'completeQualifiedReference_Ref' method will be called with a 'Model' instance and I don't find any way to access 'a' when providing proposals after 'a.', other than accessing the tokens directly. I understand that 'stmt a.' can be both a 'ReferenceStatement' with a 'QualifiedReference' as 'ref', or a 'ConflictingStatement', I guess this is causing the problem.
Is there a way to resolve this problem without writing a custom parser for the qualified references? For the 'ConflictingStatement' this would not be an issue, I would be OK with a solution where I need to parse the token used as 'name1' manually. But for the 'ReferenceStatement' this would be rather inconvenient. I would like to avoid also to replace the 'name1' and 'name2' references of the 'ConflictingStatement' with a 'Reference' and restrict the acceptable values by a validator, as it would result in a very ugly AST and the scoping rules would also be different (i.e. a 'Namespace' cannot be within a 'Variable' in a 'QualifiedReference').
If needed, a workspace with the MWE above can be downloaded from https://www.dropbox.com/s/q6y09mu0c8tqan7/xtext-contentassist-debug.zip?dl=1 .
Thanks a lot for any tips,
Daniel
|
|
|
Re: Content proposal for grammar with syntactically similar rules [message #1793862 is a reply to message #1793858] |
Fri, 17 August 2018 12:31 |
|
did you consider to unify both rules and handle the restrictions in validation and or scoping?
Model:
(declarations+=Declaration)+
statement=Statement;
Namespace:
'namespace' name=ID '{'
(declarations+=Declaration)*
'}';
Declaration: Namespace | Variable;
Variable:
'variable' name=ID;
Statement:
(
StatementReference ({ConflictingStatement.target=current}'('')'|{ReferenceStatement.ref=current})
);
StatementReference returns Reference:
'stmt' DotExpression
;
DotExpression returns Ref:
DotExpressionStart ({DotExpression.ref=current} "." tail=[Declaration])*
;
DotExpressionStart returns Ref:
{DotExpressionStart} ref=[Declaration]
;
class MyDslScopeProvider extends AbstractMyDslScopeProvider {
override getScope(EObject context, EReference reference) {
if (reference == MyDslPackage.Literals.DOT_EXPRESSION__TAIL) {
if (context instanceof DotExpression) {
val head = context.ref;
switch (head) {
DotExpressionStart:
if (head.ref instanceof Namespace) {
return Scopes::scopeFor((head.ref as Namespace).declarations)
} else {
return IScope.NULLSCOPE
}
DotExpression: {
val tail = head.tail
switch (tail) {
Variable: return IScope::NULLSCOPE
Namespace: return Scopes::scopeFor(tail.declarations)
default: return IScope::NULLSCOPE
}
}
default:
return IScope::NULLSCOPE
}
}
}
super.getScope(context, reference)
}
}
Twitter : @chrdietrich
Blog : https://www.dietrich-it.de
[Updated on: Fri, 17 August 2018 12:54] Report message to a moderator
|
|
|
|
Powered by
FUDForum. Page generated in 0.02673 seconds