diff --git a/hugo/content/docs/learn/workflow/resolve_cross_references.md b/hugo/content/docs/learn/workflow/resolve_cross_references.md index c21acfd3..97edadd8 100644 --- a/hugo/content/docs/learn/workflow/resolve_cross_references.md +++ b/hugo/content/docs/learn/workflow/resolve_cross_references.md @@ -227,3 +227,45 @@ expect(model.greetings[1].person.ref).toBe(model.persons[1]); ``` The `expect` function can be any assertion library you like. The `Hello world` example uses Vitest. + +## Handling multi-target references + +With [Langium 4.0.0](https://github.com/eclipse-langium/langium/blob/main/packages/langium/CHANGELOG.md#multi-target-references) the handling of multi-target references is supported. + +Multi-target references are cross-references that can point to multiple targets instead of just one. The AST representation changes from a single `Reference` to an `MultiReference` objects. The mechanism to resolve multi-target references is similar to single-target references. The scope provider still provides a scope for each cross-reference, but the linker now can link multiple targets from the scope to the multi-reference. + +All you need to do to change a single-target reference to a multi-target reference is to write: + +```langium +... person=[+Person:ID] ... +``` + +...instead of `person=[Person:ID]` in your grammar. + +The resolution mechanism stays the same. But be aware of the AST structure change: every `...ast.person.ref` becomes an array of references: `...ast.person.items` where each `item` is a `Reference`-like object (it has a `ref` field again). + +### When to use multi-target references? + +A typical use case for multi-target references are namespaces or partial classes. The core idea is that you want to declare multiple symbols under the same scope. Typically, these declarations are split into different files. Think of C++ `std` namespace: + +```cpp +//vector.hpp +namespace std { + class vector { ... }; +} + +//map.hpp +namespace std { + class map { ... }; +} + +//main.cpp +#include +#include +std::vector v; +std::map m; +... +``` + +As you can see, the `std` namespace is used to group related classes together, allowing for better organization and avoiding naming conflicts. In this case, both `vector` and `map` are part of the `std` namespace, and they can be used in `main.cpp` without any issues. +The multi-target reference would be used inside of the variable declarations of `v` and `m` to point to the same `std` namespace declarations. diff --git a/hugo/content/docs/reference/grammar-language.md b/hugo/content/docs/reference/grammar-language.md index 03f2c3c3..aac2747d 100644 --- a/hugo/content/docs/reference/grammar-language.md +++ b/hugo/content/docs/reference/grammar-language.md @@ -174,7 +174,35 @@ Hello Sara ! ``` will result in an error message since the cross reference resolution will fail because a `Person` object with the name 'Sara' has not been defined, even though 'Sara' is a valid `ID`. +##### Multi-Target Cross-References + +Some language concepts are able to be defined multiple times, for example namespaces or partial classes - symbols that are the same, but are normally distributed over multiple files or locations in the same file. In such cases, it is useful to be able to reference all definitions of such a symbol instead of just one. + +Langium supports multi-target cross-references by using the `[+Type:ID]` syntax: + +```langium +entry Model: + (persons+=Person | greetings+=Greeting)*; +Person: + 'person' name=ID; +Greeting: + 'Hello' person=[+Person:ID] '!'; +``` + +Given the following file content you will get two `Person` objects created, both named "Bob", and a `Greeting` object that references both of them: + +```plain +person Bob +person Bob +Hello Bob ! +``` + +Programmatically, the `person` property of the `Greeting` object will be an array of references to both `Person` objects `greeting.person.items[0].ref` and `greeting.person.items[1].ref`. + +Resolution-wise, single-target and multi-target cross-references behave the same: if no matching target is found, a diagnostic error is reported. The resolution itself uses the `ScopeProvider` and the `ScopeComputation` services, which can be customized as needed. + #### Unassigned Rule Calls + Parser rules do not necessarily need to create an object, they can also refer to other parser rules which in turn will be responsible for returning the object. For example, in the [Arithmetics example](https://github.com/eclipse-langium/langium/blob/main/examples/arithmetics/src/language-server/arithmetics.langium): ```langium