Xtext Tip: How to use fully qualified names in your language

Xtext allows to easily add support for fully qualified names in your language. The full code for this example can be found here.

The basic block language

To illustrate this feature I will use a simple language to represent blocks. Each block has a name and can contain three members:

  • Fields, which are basically a name,
  • Blocks, which are blocks contained in another block
  • Aliases, which allow to give another name to another member

This is an example of our block language:

block Block1 {
	
	field field1
	field field2

	block SubBlock {
		field field2
	}
	alias field3 aliases field2

}
block Block2 {
	alias field1 aliases Block1.SubBlock.field2 // this does not compile because it aliases an FQN
}

This is the grammar that we used to generate it:

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

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

Model:
	blocks+=Block*;

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

Member:
	Block | Field | Alias;

Field:
	'field' name=ID;

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

The block language with FQN support

Xtext is based around certain conventions. By convention (this convention can be changed), Xtext assumes that FQN of a member is the concatenation of the names of the containers using a dot (.) as separator. For example, the FQN of the field2 field in the Subblock container would be Block1.SubBlock.field.

Hence, if we want to have the possibility to have a reference named with an FQN, then we need to do two things:

  1. Create a rule that matches an FQN.
  2. Use the rule to tell Xtext that a given reference can be referenced by the FQN.

This can be seen in the following grammar:

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

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

Model:
	blocks+=Block*;

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

Member:
	Block | Field | Alias;

Field:
	'field' name=ID;

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

MemberFQN: // this rules matches either an FQN or a simple identifier
	ID ("." ID)*;

Because of the convention, changing the grammar is the only step necessary to support FQNs like this.

Limitations

This only works if the FQN is a set of segments separated by dots, and where each segment correspond to the containing object. If the FQN has another structure, further customization is needed.

comments powered by Disqus