xtext couldn't resolve reference to Model - xtext

I'm trying to create a grammar for my dsl and this is a sample from it.
Model :
'#Model' modelName=ID '{'
fields+=Field*
toBeImportedIn+=ModelExportList*
'}'
;
/* Some other Rules here */
WebServiceConsumer :
'#WebServiceConsumer' '(' serviceName=ID ',' webServiceURL=STRING ','
modelName=[Model])'
;
When I try to test my grammar like this, I got an error : "Couldn't resolve reference to Model 'myModel'."
#Model myModel{}
#WebServiceConsumer(serviceName,"URL goes here",myModel)

you can reference things that have an attribute called name by default. you can bypass this behaviour by implementing your own IQualifiedNameProvider e.g.
package org.xtext.example.mydsl;
import org.eclipse.xtext.naming.DefaultDeclarativeQualifiedNameProvider;
import org.eclipse.xtext.naming.QualifiedName;
import org.xtext.example.mydsl.myDsl.Element;
import org.xtext.example.mydsl.myDsl.Package;
public class MyDslQNP extends DefaultDeclarativeQualifiedNameProvider{
QualifiedName qualifiedName(Element e) {
Package p = (Package) e.eContainer();
return QualifiedName.create(p.getName(), e.getId());
}
}
and dont forget to bind
public class MyDslRuntimeModule extends org.xtext.example.mydsl.AbstractMyDslRuntimeModule {
override Class<? extends IQualifiedNameProvider> bindIQualifiedNameProvider() {
return MyDslQNP;
}
}

Related

Xtext problem referencing grammar A from validator of grammar B

In Xtext, how do I follow a reference from grammar B to grammar A, within a validator of grammar B (which is in the ui-plugin)? Consider the following example.
Grammar A is org.xtext.people.People
grammar org.xtext.people.People with org.eclipse.xtext.common.Terminals
generate people "http://www.xtext.org/people/People"
People:
people+=Person*;
Person:
'person' name=ID ';';
and an instance
person Alice {citizenship "MN"; id "12345"; }
person Bob {citizenship "CH"; id "54321";}
person Malice {citizenship "XXX"; id "66666"; }
At an airport, entries of people are recorded.
enter Alice;
enter Bob;
enter Malice;
Entries are modelled with a second grammar B org.xtext.entries.Entries
grammar org.xtext.entries.Entries with org.eclipse.xtext.common.Terminals
generate entries "http://www.xtext.org/entries/Entries"
import "http://www.xtext.org/people/People"
Entries:
entries+=Entry*;
Entry:
'enter' person=[Person] ';';
After ensuring that the Eclipse project org.xtext.entries has the project org.xtext.people on it's classpath, and ensuring that the org.xtext.entries plugin has the org.xtext.people as a dependency, all works as expected.
There is a travel ban on people from country XXX, although certain deserving people are excluded. Only the CIA knows who is excluded from the ban. Entries must not be allowed for people from XXX unless excluded.
The updated grammar is
grammar org.xtext.entries.Entries with org.eclipse.xtext.common.Terminals
generate entries "http://www.xtext.org/entries/Entries"
import "http://www.xtext.org/people/People"
Entries:
entries+=Entry*;
Entry:
travelBanOverride=TravelBanOverride?
'enter' person=[Person] ';';
TravelBanOverride: '#TravelBanOverride' '(' code=STRING ')';
with validator
package org.xtext.entries.validation
import org.eclipse.xtext.validation.Check
import org.xtext.entries.entries.EntriesPackage
import org.xtext.entries.entries.Entry
import org.xtext.entries.CIA
class EntriesValidator extends AbstractEntriesValidator {
public static val BAN = 'BAN'
public static val ILLEGAL_OVERRIDE = 'ILLEGAL_OVERRIDE'
#Check
def checkBan(Entry entry) {
if (entry.person.citizenship == "XXX") {
if (entry.travelBanOverride === null) {
error('Violation of Travel Ban', EntriesPackage.Literals.ENTRY__PERSON, BAN)
}
else {
val overridecode = entry.travelBanOverride.code;
val valid = CIA.valid(entry.person.name, entry.person.id, overridecode)
if (!valid) {
error('Illegal override code', EntriesPackage.Literals.ENTRY__TRAVEL_BAN_OVERRIDE, ILLEGAL_OVERRIDE)
}
}
}
}
}
where the driver for the external CIA web-app is modelled for example by
package org.xtext.entries;
public class CIA {
public static boolean valid(String name, String id, String overrideCode) {
System.out.println("UNValid["+name+","+overrideCode+"]");
return name.equals("Malice") && id.equals("66666") && overrideCode.equals("123");
}
}
The validations work as expected.
I now wish to provided a quick-fix for BAN, that checks for an override code from the CIA.
package org.xtext.entries.ui.quickfix
import org.eclipse.xtext.ui.editor.quickfix.DefaultQuickfixProvider
import org.eclipse.xtext.ui.editor.quickfix.Fix
import org.xtext.entries.validation.EntriesValidator
import org.eclipse.xtext.validation.Issue
import org.eclipse.xtext.ui.editor.quickfix.IssueResolutionAcceptor
import org.xtext.entries.entries.Entry
import org.xtext.entries.Helper
class EntriesQuickfixProvider extends DefaultQuickfixProvider {
#Fix(EntriesValidator.BAN)
def tryOverride(Issue issue, IssueResolutionAcceptor acceptor) {
acceptor.accept(issue, 'Try override', 'Override if CIA says so.', 'override.png')
[element ,context |
val entry = element as Entry
// val person = entry.person // no such attribute
//val person = Helper.get(entry); // The method get(Entry) from the type Helper refers to the missing type Object
]
}
}
The first commented line does not compile: there is no attribute person. The second commented line is an attempt to solve the problem by getting a helper class in org.xtext.entries to get the person, but this does not compile either, giving a "The method get(Entry) from the type Helper refers to the missing type Object" error message.
For completeness, here is that helper.
package org.xtext.entries
import org.xtext.people.people.Person
import org.xtext.entries.entries.Entry
class Helper {
static def Person get(Entry entry) {
return entry.person;
}
}
Further, entry.travelBanOverride compiles fine, but entry.person does not. Clicking on Entry in Eclipse takes one to the expected code, which has both travelBanOverride and person.
The issue does not occur with a Java class in the same project and package.
package org.xtext.entries.ui.quickfix;
import org.xtext.entries.entries.Entry;
import org.xtext.people.people.Person;
public class Test {
public static void main(String[] args) {
Entry entry = null;
Person p = entry.getPerson();
}
}
Rewriting the quickfix in Java solves the problem.
package org.xtext.entries.ui.quickfix;
import org.eclipse.xtext.ui.editor.quickfix.DefaultQuickfixProvider;
import org.eclipse.xtext.ui.editor.quickfix.Fix;
import org.xtext.entries.validation.EntriesValidator;
import org.eclipse.xtext.validation.Issue;
import org.eclipse.xtext.ui.editor.quickfix.IssueResolutionAcceptor;
import org.xtext.entries.entries.Entry;
import org.xtext.entries.Helper;
import org.eclipse.xtext.ui.editor.model.edit.IModificationContext;
import org.eclipse.xtext.ui.editor.model.edit.ISemanticModification;
import org.eclipse.emf.ecore.EObject;
import org.xtext.entries.entries.Entry;
import org.xtext.people.people.Person;
public class EntriesQuickfixProvider extends DefaultQuickfixProvider {
#Fix(EntriesValidator.BAN)
public void tryOverride(final Issue issue, IssueResolutionAcceptor acceptor) {
acceptor.accept(issue,
"Try to override",
"Override",
"override.gif",
new ISemanticModification() {
public void apply(EObject element, IModificationContext context) {
Entry entry = (Entry) element;
System.out.println(entry.getPerson());
}
}
);
}
}
How do I follow a reference from grammar B (Entries) to grammar A (People), within a validator of grammar B?
My mistake is the following.
After ensuring that the Eclipse project org.xtext.entries has the
project org.xtext.people on it's classpath, and ensuring that the
org.xtext.entries plugin has the org.xtext.people as a dependency, all
works as expected.
The org.xtext.entries.ui ui-plugin must also have the org.xtext.people on its Java (Eclipse project) build path. Exporting and making a plugin-dependency it not enough.
Note that this setting should be made early, before crafting the quick-fix, because the Xtend editor has refreshing issues.

Nancy register dependency with type argument

Nancy auto-registration of dependencies is having trouble resolving a dependency with a type argument, so I'm trying to manually register it and cannot figure it out.
public abstract class BaseE { }
public abstract class BaseS<T> where T : BaseE { }
public class E : BaseE { }
public interface ISomething { }
public class S<T> : BaseS<T> where T : BaseE, ISomething { }
I want ISomething to be auto-resolved to class S, but that's not working, so I created a custom bootstrapper:
public class CustomBootstrapper : DefaultNancyBootstrapper
{
protected override void ApplicationStartup(TinyIoCContainer container, IPipelines pipelines)
{
base.ConfigureApplicationContainer(container);
// Works fine, but not sure if necessary
container.Register<E, BaseE>();
// Cannot get the syntax right, doesn't compile
// ??? container.Register<ISomething, S<E>>();
}
}
I can't seem to figure out the syntax. The line container.Register<ISomething, S<E>>(); gives me a compile error: The type S<E> cannot be used as type parameter 'RegisterImplementation' in the generic type or method 'TinyIoCContainer.Register<RegisterType, RegisterImplementation()'. There is no implicit reference conversion from 'S<E>' to 'ISomething'
Please help me figure out what the correct syntax / correct way to register a dependency with a type argument.
The error was due to incorrect syntax as pointed out by qujck.
public class S<T> : BaseS<T> where T : BaseE, ISomething { }
should be
public class S<T> : BaseS<T>, ISomething where T : BaseE { }
The first syntax says that type T extends BaseE AND implements ISomething, the second syntax says that type T only extends BaseE (and S implements ISomething). Dependency auto-injection is now working as expected.

Xtext - Couldn't resolve reference to JvmOperation

I am trying to create a small DSL language using XText.
grammar org.xtext.example.mydsl.MyDsl with org.eclipse.xtext.xbase.Xbase
import "http://www.eclipse.org/xtext/common/JavaVMTypes" as jvmTypes
generate myDsl "http://www.xtext.org/example/mydsl/MyDsl"
StartState:
'startState'
'evaluate' ref=JvmTypeReference "-" op=[jvmTypes::JvmOperation]
'end';
Following is the ScopeProvider Implementation:
public class MyScopeProvider extends AbstractDeclarativeScopeProvider {
IScope scope_StartState_op(StartState call, EReference reference) {
JvmType type = call.getRef().getType();
List<IEObjectDescription> descriptions = new ArrayList<IEObjectDescription>();
if (type instanceof JvmGenericType) {
JvmGenericType gt = (JvmGenericType) type;
for (JvmMember member : gt.getMembers()) {
if (member instanceof JvmOperation) {
descriptions.add(EObjectDescription.create(member.getSimpleName(), member));
}
}
}
return new SimpleScope(descriptions);
}}
I type following into the resulting editor
startState evaluate controller.Controller - perform end
Here I am able to get the code completion working for method (perform which is in controller.Controller class) as expected. But I am in need of help to resolve the following error which happens after the code completion.
Couldn't resolve reference to JvmOperation 'perform'.
Also, I tried following Peter's Blog with no success
Posting answer to my own question. The grammer should look like the following.
grammar org.xtext.example.mydsl.MyDsl with org.eclipse.xtext.xbase.Xtype
import "http://www.eclipse.org/xtext/common/JavaVMTypes" as jvmTypes
generate myDsl "http://www.xtext.org/example/mydsl/MyDsl"
StartState:
'startState'
'evaluate' ref=JvmTypeReference "-" op=[jvmTypes::JvmOperation]
'end';
There is no change to the scope provider and the usage.

Xtext Grammar mixins result in Guice injection error

I am writing an Xtext grammar that can access documentation that are declared before a functions.
Our current grammar defines hidden(ML_COMMENT, SL_COMMENT,...) with:
ML_COMMENT: '/*' -> '*/'
SL_COMMENT: '//' -> EOL
I have now created a second Xtext project, with the following grammar:
grammar my.DocumentationGrammar with my.OriginalGrammar hidden(WS, FUNCTION_BODY, EOL, SL_COMMENT)
import "http://www.originalGrammar.my"
generate documentationGrammar "http://www.documentationGrammar.my"
/* Parser rules */
TranslationUnit:
eds+=DoxExternalDefinition*
;
DoxExternalDefinition:
def = Definition
| lib = CtrlLibUsage
| comment=ML_COMMENT
;
FunctionDefinition:
aml=AccessModifiersList ts=TypeSpecifier? f=Function '(' pl=ParameterTypeList? ')' /* cs=CompoundStatement */ // the compound statement is ignored
;
//terminal DOXYGEN_COMMENT: ML_COMMENT;
terminal FUNCTION_BODY: '{' -> '}';
I have created the dependency in the plugin and added this to the
bean = StandaloneSetup {
scanClassPath = true
platformUri = "${runtimeProject}/.."
// The following two lines can be removed, if Xbase is not used.
registerGeneratedEPackage = "org.eclipse.xtext.xbase.XbasePackage"
registerGenModelFile = "platform:/resource/org.eclipse.xtext.xbase/model/Xbase.genmodel"
// we need to register the super genmodel
registerGeneratedEPackage = "my.OriginalGrammar.OriginalGrammarPackage"
registerGenModelFile = "platform:/resource/my.OriginalGrammar/model/generated/OriginalGrammar.genmodel"
}
Now in my third plugin project, I want to access this parser in a Standalone fashion. So I created the following Parser file (based on this example: http://davehofmann.de/blog/?p=101) :
import java.io.IOException;
import java.io.Reader;
import java.nio.file.Path;
import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.xtext.parser.IParseResult;
import org.eclipse.xtext.parser.IParser;
import org.eclipse.xtext.parser.ParseException;
import org.eclipse.xtext.resource.XtextResource;
import org.eclipse.xtext.resource.XtextResourceSet;
import my.DocumentationGrammar.DocumentationGrammarStandaloneSetup;
import com.google.inject.Inject;
import com.google.inject.Injector;
public class DoxygenParser {
#Inject
private IParser parser;
private Injector injector;
public DoxygenParser() {
setupParser();
}
private void setupParser() {
injector = new DocumentationGrammarStandaloneSetup().createInjectorAndDoEMFRegistration();
injector.injectMembers(this);
}
/**
* Parses data provided by an input reader using Xtext and returns the root node of the resulting object tree.
* #param reader Input reader
* #return root object node
* #throws IOException when errors occur during the parsing process
*/
public EObject parse(Reader reader) throws IOException
{
IParseResult result = parser.parse(reader);
if(result.hasSyntaxErrors())
{
throw new ParseException("Provided input contains syntax errors.");
}
return result.getRootASTElement();
}
}
However, when I try to run it, I receive Guice Injection errors saying that
com.google.inject.ProvisionException: Guice provision errors:
1) Error injecting constructor, org.eclipse.emf.common.util.WrappedException: java.lang.RuntimeException: Cannot create a resource for 'classpath:/my/documentationGrammar/DocumentationGrammar.xtextbin'; a registered resource factory is needed
I know that the parser "should" be correct, since when I use the OriginalGrammarStandaloneSetup it works perfectly fine.
You have to make sure that your sublanguage also invokes the standalone setup of your super language. Usually this is generated in the DocumentationGrammarStandaloneSetupGenerated class, but please make sure that this was properly setup by Xtext. In the end, there should be something like
if (!Resource.Factory.Registry.INSTANCE.getExtensionToFactoryMap().containsKey("xtextbin"))
Resource.Factory.Registry.INSTANCE.getExtensionToFactoryMap().put(
"xtextbin", new BinaryGrammarResourceFactoryImpl());
in the callchain of your setup's createInjectorAndDoEMFRegistration method.

Xtext cross reference using custom terminal rule

I'm trying to get cross-references to work in my DSL. Here's a stripped down version of the grammar (a modified version of the standard example DSL):
grammar org.xtext.example.mydsl.MyDsl with org.eclipse.xtext.common.Terminals
generate myDsl "http://www.xtext.org/example/mydsl/MyDsl"
Releases:
releases+=Release*
;
terminal VERSION : ('0'..'9')+'.'('0'..'9')+('.'('0'..'9'|'x')+)?;
Release:
'release' version = VERSION ('extends' parent = [Release|VERSION])?
;
Since I'm not using the standard name = ID pattern, I followed this blog post about how to create my own IQualifiedNameProvider:
public class MyDslQNP extends DefaultDeclarativeQualifiedNameProvider {
QualifiedName qualifiedName(Release e) {
Package p = (Package) e.eContainer();
return QualifiedName.create(p.getName(), e.getVersion());
}
}
From another answer on SO, I gathered that I should implement my own scope provider:
public class MyDslScopeProvider extends AbstractDeclarativeScopeProvider {
IScope scope_Release_parent(Release release, EReference ref) {
Releases releases = (Releases) release.eContainer();
return Scopes.scopeFor(releases.getReleases());
}
}
I've also bound these in the runtime module:
public class MyDslRuntimeModule extends
org.xtext.example.mydsl.AbstractMyDslRuntimeModule {
#Override
public Class<? extends IQualifiedNameProvider> bindIQualifiedNameProvider() {
return MyDslQNP.class;
}
#Override
public Class<? extends IScopeProvider> bindIScopeProvider() {
return MyDslScopeProvider.class;
}
}
When running the generated editor I create a file which looks like this:
release 1.2.3
release 1.2.2 extends 1.2.3
The problem is that (1) the editor won't autocomplete on the 'extends' clause, and (2) the editor shows an error message Couldn't resolve reference to Release '1.2.3'.
What am I missing?
Your QualifiedNameProvider seems to create bogus qualified names, e.g. the name for the release 1.2.2 would have two segments [release][1.2.2] where the lookup will search for [release][1][2][2].
Please try to create a proper qualified name like this:
QualifiedName qualifiedName(Release e) {
Package p = (Package) e.eContainer();
QualifiedName release = getConverter().toQualifiedName(e.getVersion());
return QualifiedName.create(pack.getName()).append(releaseSuffix);
}
Scopes.scopeFor is a static method and therefore doesn't use the configured IQualifiedNameProvider.
You need to pass it in explicitly using
Scopes.scopeFor(Iterable, Function, IScope)
But in your case you don't need the special handling in the scope provider at all, since local elements with a qualified name provider are being put on the scope automatically.

Resources