Skip to content

Commit

Permalink
documentation: Document IDerivedStateComputer
Browse files Browse the repository at this point in the history
Signed-off-by: Tommaso Fonda <[email protected]>
  • Loading branch information
tfonda-fbk committed Aug 16, 2024
1 parent 798ad4a commit 8061b9f
Showing 1 changed file with 81 additions and 0 deletions.
81 changes: 81 additions & 0 deletions xtext-website/documentation/303_runtime_concepts.md
Original file line number Diff line number Diff line change
Expand Up @@ -632,6 +632,87 @@ public interface ITokenStream {
}
```

## Tree Manipulation {#tree-manipulation}
An implementation of [IDerivedStateComputer](https://github.com/eclipse/xtext/blob/main/org.eclipse.xtext/src/org/eclipse/xtext/resource/IDerivedStateComputer.java) can be used to modify the objects in the abstract syntax tree after parsing. Commonly, implementations of this interface are used to modify the objects' fields programmatically.

As an example, let's take a simple Entity DSL defined by the following grammar:

```xtext
grammar org.xtext.example.mydsl.MyDsl with org.eclipse.xtext.common.Terminals
generate myDsl "http://www.xtext.org/example/mydsl/MyDsl"
Model:
entities+=Entity*;
Entity:
'Entity' (name=ID | name=ID? 'overrides' overriddenEntity=[Entity]);
```

This grammar allows declaring that an Entity overrides another Entity. In this case, by design and for the sake of conciseness it is not required to explicitly name the overriding Entity. However, you might want to reference an unnamed overriding Entity later, e.g. to override it in turn. This is where IDerivedStateComputer comes in handy.

Let's see a sample implementation of this interface that takes care of setting the `name` field of unnamed Entities. We want to set it to the name of the overridden Entity, suffixed with "_overridden". For this purpose, we write the following implementation and place it, for instance, inside the `org.xtext.example.mydsl.services` package of our DSL project:

```java
package org.xtext.example.mydsl.services;

import org.eclipse.xtext.resource.DerivedStateAwareResource;
import org.eclipse.xtext.resource.IDerivedStateComputer;
import org.xtext.example.mydsl.myDsl.Entity;

public class MyDslDerivedStateComputer implements IDerivedStateComputer {
private static final String SUFFIX = "_overridden";

@Override
public void installDerivedState(DerivedStateAwareResource resource, boolean preLinkingPhase) {
resource.getAllContents().forEachRemaining(eObject -> {
if (eObject instanceof final Entity entity) {
if (entity.getName() == null) {
entity.setName(entity.getOverriddenEntity().getName() + SUFFIX);
}
}
});
}

@Override
public void discardDerivedState(DerivedStateAwareResource resource) {
resource.getAllContents().forEachRemaining(eObject -> {
if (eObject instanceof final Entity entity) {
final String name = entity.getName();
if (name != null && name.endsWith(SUFFIX)) {
final String nameWithoutSuffix = name.substring(0, name.length() - SUFFIX.length());
final Entity overriddenEntity = entity.getOverriddenEntity();
if (overriddenEntity != null && nameWithoutSuffix.equals(overriddenEntity.getName())) {
entity.setName(null);
}
}
}
});
}
}
```

The `installDerivedState()` method is responsible for applying the desired changes. For serialization purposes, we also need to implement `discardDerivedState()` to tell the framework how to revert the changes applied programmatically. Note that this implementation unsets the `name` field of any Entity whose name coincides with that of the overridden Entity, plus the suffix, regardless of the origin of such name (input by the user or set by `installDerivedState()`).

To have Xtext actually use our custom IDerivedStateComputer, as a last thing we need to bind it in our runtime module:

```java
public class MyDslRuntimeModule extends AbstractMyDslRuntimeModule {
public Class<? extends IDerivedStateComputer> bindIDerivedStateComputer() {
return MyDslDerivedStateComputer.class;
}

@Override
public Class<? extends XtextResource> bindXtextResource() {
return DerivedStateAwareResource.class;
}

public Class<? extends IResourceDescription.Manager> bindIResourceDescriptionManager() {
return DerivedStateAwareResourceDescriptionManager.class;
}
}
```

## Formatting {#formatting}

Formatting (aka. pretty printing) is the process of rearranging the text in a document to improve the readability without changing the semantic value of the document. Therefore a formatter is responsible for arranging line-wraps, indentation, whitespace, etc. in a text to emphasize its structure, but it is not supposed to alter a document in a way that impacts the semantic model.
Expand Down

0 comments on commit 8061b9f

Please sign in to comment.