Xtext Tip: Adding importedNamespace support

If you look at Xtext’s documentation here, there is a mention of the possibility of adding import name space support to your DSLs. This allows your DSL to have Java like imports. For example, in our block language, it should be possible to import the name space from another Block and then use in our alias. We can for example define Block3 in file Block3.block

block Block3 {
		field myField1
		field myField2
}

And then use it in the file Block1.block as follows:

block Block1 {
	import Block3.*
 	
	alias myAlias1 aliases myField1
	alias myAlias2 aliases myField2
}

block Block2 {
 	import Block3.myField2
	alias myAlias2 aliases myField2
}

The block language with import name space support

The first step is to modify the MWE file to enable the ImportNameSpaceScopeProvider, this is done as follows:

module com.idiomaticsoft.dsl.block.GenerateBlock

import org.eclipse.xtext.xtext.generator.*
import org.eclipse.xtext.xtext.generator.model.project.*

var rootPath = ".."

Workflow {

	component = XtextGenerator {
		configuration = {
			project = StandardProjectConfig {
				baseName = "com.idiomaticsoft.dsl.block"
				rootPath = rootPath
				runtimeTest = {
					enabled = true
				}
				eclipsePlugin = {
					enabled = true
				}
				eclipsePluginTest = {
					enabled = true
				}
				createEclipseMetaData = true
			}
			code = {
				encoding = "UTF-8"
				lineDelimiter = "\n"
				fileHeader = "/*\n * generated by Xtext \${version}\n */"
				preferXtendStubs = false
			}
		}
		language = StandardLanguage {
			name = "com.idiomaticsoft.dsl.block.Block"
			fileExtensions = "block"
			serializer = {
				generateStub = false
			}
			validator = {
				composedCheck = "org.eclipse.xtext.validation.NamesAreUniqueValidator"// Generates checks for @Deprecated grammar annotations, an IssueProvider and a corresponding PropertyPage
				generateDeprecationValidation = true
			}
			generator = {
				generateXtendStub = true
			}
			scopeProvider = scoping.ImportNamespacesScopingFragment2 {
				generateXtendStub = false
			}
			junitSupport = {
				junitVersion = "5"
			}
		}
	}
}

Once we have that, we need to modify the grammar. A new grammar would look like this:

grammar com.idiomaticsoft.dsl.block.Block with org.eclipse.xtext.common.Terminals

generate block "http://www.idiomaticsoft.com/dsl/block/Block"

Model:
	blocks+=Block*;

Import:
	'import' importedNamespace=ImportFQN;

Block:
	'block' name=ID '{'
	(imports+=Import)*
	(members+=Member)*
	'}';

Member:
	Block | Field | Alias;

Field:
	'field' name=ID;

Alias:
	'alias' name=ID 'aliases' alias=[Member|MemberFQN];

MemberFQN:
	ID ("." ID)*;

ImportFQN:
	ID ("." ID)* ('.' (ID | '*'));

This grammar includes a new Import rule that has importedNamespace attribute. This attribute is one of those Xtext defaults, and to make the infrastructure work, you need to name your attribute like that. You can see that the new grammar supports both importing an FQN and a wildcard, without much more effor from the user.

Things to consider

The default for this feature are the following:

  1. If you want to have several importedNamespace, then you need to use several Import rules. You cannot have a rule like importedNamespace+=ImportFQN. The default implementation expects this attribute to be a string.
  2. The imports of the rules are valid for all elements in the container and its children.

The full code for this example can be found here.

comments powered by Disqus