Xtext Tip: Add custom values to your DSL

Sometimes we have a value that has different representations. For example, an integer can be represented in base 10, base 2, base 16 or any other. Depending of the domain, one of these representations might mean more to the experts. Xtext allows to do this very easily. The full code for this example can be found here.

The hex language

To illustrate this feature we are using a DSL to represent a binary file as a chain of hexadecimal values. A file of this language looks like follows:

0x00 0x1A 0xA1

We want this model to be stored as a list of the integer values that are represented by the list of hexadecimal values. The grammar for this language is the following:

grammar com.idiomaticsoft.dsl.hex.Hex with org.eclipse.xtext.common.Terminals

generate hex "http://www.idiomaticsoft.com/dsl/hex/Hex"
import "http://www.eclipse.org/emf/2002/Ecore" as ecore

Model:
	hex+=Hex*;

Hex:
	value=HEX_VALUE;

// we have hexadecimal values of the type 0xXX, where X is number from 0 .. 1 or a letter from A to F
terminal HEX_VALUE returns ecore::EInt:
	'0x' (('0'..'9') | ('a'..'z') | ('A'..'Z')) (('0'..'9') | ('a'..'z') | ('A'..'Z'));

Customize the value parser

If you generate the code for this grammar, the editor will not recognize any valid file. Indeed, Xtext does not know how to convert a value of the form 0xXX to an integer. To do this we first need to create a ValueConverter:

package com.idiomaticsoft.dsl.hex.converter;

import org.eclipse.xtext.common.services.DefaultTerminalConverters;
import org.eclipse.xtext.conversion.IValueConverter;
import org.eclipse.xtext.conversion.ValueConverter;
import org.eclipse.xtext.conversion.ValueConverterException;
import org.eclipse.xtext.nodemodel.INode;

public class MyValueConverter extends DefaultTerminalConverters {

	@ValueConverter(rule = "HEX_VALUE")
	public IValueConverter<Integer> HexValue() {
		return new HexValueConverter();
	}

	public static class HexValueConverter implements IValueConverter<Integer> {

		@Override
		public Integer toValue(String string, INode node) throws ValueConverterException {
			return Integer.parseInt(string.substring(2), 16);
		}

		@Override
		public String toString(Integer value) throws ValueConverterException {
			return "0x" + Integer.toHexString(value);
		}
	}
}

The ValueConverter transforms an hex string to an integer and an integer to an hex string. The transformation from integer to hex string is particularly useful when we are serializing a language from an in memory model. In this case we are creating a value converter for the terminal HEX_VALUE, this can be done for any terminal in your grammar. The annotation @ValueConverter is used to point to the right terminal rule.

Second, we create configure Xtext to use this class instead of the default one. This is done in the RuntimeModule:

/*
 * generated by Xtext 2.26.0
 */
package com.idiomaticsoft.dsl.hex;

import org.eclipse.xtext.conversion.IValueConverterService;

import com.idiomaticsoft.dsl.hex.converter.MyValueConverter;

/**
 * Use this class to register components to be used at runtime / without the
 * Equinox extension registry.
 */
public class HexRuntimeModule extends AbstractHexRuntimeModule {

	@Override
	public Class<? extends IValueConverterService> bindIValueConverterService() {
		return MyValueConverter.class;
	}

}

Once we have this, we can enjoy our new converters.

comments powered by Disqus