Xtext Tip: Use validation to enforce constraints

Sometimes, you need some constraints for your DSL and you can use the grammar language for that. However, this is not a good idea. For example, in a previous post, I created a grammar rule to parse hexadecimal numbers with exactly two digits. To do this, we created the following rule:

terminal HEX_VALUE returns ecore::EInt:
	'0x' (('0'..'9') | ('a'..'z') | ('A'..'Z')) (('0'..'9') | ('a'..'z') | ('A'..'Z'));

This rules does exactly that, but when it is not valid it shows a cryptic error. The error does not help the user to fix this.

Invalid number of digits in hexadecimal value.
Figure 1.- Invalid number of digits in hexadecimal value.

A more ergonomic solution is to create more flexible grammar and then add a validation. A more flexible rule for our hexadecimal value would be:

terminal HEX_VALUE returns ecore::EInt:
	'0x' (('0'..'9') | ('a'..'z') | ('A'..'Z'))*;

Normally, we would add a check in the validation file created by Xtext. However, the error here will happen before the validation, it will happen at conversion time. To fix this, we can enhance our ValueConverter as follows:

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 {
			if (string.substring(2).length() != 2) {
				throw new ValueConverterException("The value " + string + " is invalid. Please use a value of the form '0xXX'.", node, null);
			}
			return Integer.parseInt(string.substring(2), 16);
		}

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

After this change, we provide the user with a more ergonomic error message.

Image showing an ergonomic error in the parsing of the hexadecimal value.
Figure 1.- Ergonomic error.

The full code for this example can be found here.

comments powered by Disqus