Xtext workaround to perform a semantic predicate

Hi all,

I am currently working on mapping the concepts of JSON Schema (https://json-schema.org/) to Ecore+OCL, and then, generating an Xtext grammar that has json as concrete syntax.

We have a problem with the specifications of PatternProperties [3]. According to the Pattern Properties specifications, when a key matches a given regular expression [4], its value has to conform to a given schema.

In the excerpt of the grammar shown below, when a key matches the regex “a|b”, the value has to be parsed as (Some other schema 1), and when a key matches “c|d” the value has to be parsed as (Some other schema 2).

Properties returns Properties:
{Properties}
‘{’ ( property+=PatternProperties ( ‘,’ property += PatternProperties)* )? ‘}’;

PatternProperties returns PatternProperties:
=>PatternProperties1 | =>PatternProperties2;

// key has to match the regex “a|b”
PatternProperties1 returns PatternProperties1:
{PatternProperties1} key = EString ‘:’ value= ( Some other schema 1) ;

// key has to match the regex “c|d”
PatternProperties2 returns PatternProperties2:
{PatternProperties2} key = EString ‘:’ value= ( Some other schema 2) ;

This case could be solved with semantic predicates, where the condition would be the key matching with the given regular expression. But as far as we know, Xtext does not support the semantic predicate [1].

We have been trying different workarounds, like [2] without success.

Is there any workaround in Xtext that solves our problem?

Thank you in advance.

Kind regards,
Antonio

PS: We also post this in the Xtext forum: Eclipse Community Forums: TMF (Xtext) » Workaround semantic predicates

[1] Eclipse Community Forums: TMF (Xtext) » problem of semantic predicates in Xtext
[2] Getting alternative cross-references to work with existing EPackages | Meinte's DSL Blog
[3]object — Understanding JSON Schema 2020-12 documentation
[4]Regular Expressions — Understanding JSON Schema 2020-12 documentation

If I understood your problem right, the language, you want to express as Xtext grammar is not context-free. So, you cannot use a context-free grammar only to express it. You need to add semantic validation.

A common practice in Xtext (and language implementation in general) is to make the context-free grammar accepting a super set of your real language. Subsequently, in the validation phase your are going to check the parsed input semantically and with that allowing only those subset which corresponds to your real language. This is very similar to a type check. And in fact, what you want to achieve is a type check at the end: to check whether value part of the property is matching the JSON schema selected by the regular expression of the key part of the property.

In Xtext, you would need to specify one grammar rule for PatternProperties which parses all kinds of schemas at the value part.
Secondly, you need a validation rule (AKA @Check method) which validates that if the parsed key value matches the regular expression then the parsed value must match the JSON schema assigned to that regular expression.

The question that remains is how to get the JSON schema validation of the value part implemented which gets invoked at the validation rule method. As the validation rule gets applied after parsing has taken place, this may not be easy to achieve, however.
But, as you are generating the Xtext grammar either, you may generate several Xtext grammars, one per PatternProperty instance. And then apply the generated parser to the text sequence of the property value at the validation rule method according to the matching regular expression.

Alternatively, you may generate code for a structural check of the JSON parse tree of the value part of the property. This may be easier at the end.

Denis

Hi Denis,

Thanks a lot! Sorry for the late reply, but your post gives us a lot of things to think about :slight_smile: .

I think that we implemented what you suggest in validation rule (@Check method). The code is something like this:

	@Check
	public void checkPatternProperties2(PatternPropertiesReference ppr) {		
		if (ppr.getKey().contains("a")) {
			//Get the String from the node
			String pprString= NodeModelUtils.getTokenText(NodeModelUtils.getNode(ppr));
			//Create new ResourceSet and Resource
			ResourceSet reset = new ResourceSetImpl();
			Resource resource = reset.createResource(URI.createURI("platform:/plugin/sample.test8/example.mydsl9"));
			//Create the InputStream using the obtained String from the node
			InputStream in = new ByteArrayInputStream(pprString.getBytes());
			try {
				resource.load(in, reset.getLoadOptions());
			} catch (IOException e) {
				e.printStackTrace();				
			}
			//Get the errors & error(pprString, null)
			resource.getErrors();			
			//Get the new PatternProperties
			PatternProperties2 ppr2 = (PatternProperties2) resource.getContents().get(0);
			//Container		
			PatternProperties pprRoot = (PatternProperties) ppr.eContainer();						
			//Remove all elements
			pprRoot.getProperty().remove(ppr);
			//Add new one
			pprRoot.getProperty().add(ppr2);			
			}
	}

So, we create several Xtext grammars in the same project but these grammars rely on the same meta-model. Do you think that this code is somehow correct? Is what you expect?

The other question is regarding if we should remove the previous element and add the new one. Is this seem correct to you? It seems to us that in Xtext this does not work well in some cases (we are still doing some tests)

The final question is if you know some literature about this. Would you advise us of any paper or book?

Thanks again.

Kind regards,
Antonio