You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何为语法规则集成Scope Provider与自定义名称?

Solution: Custom Scope Provider for Xtext Visibility Restriction

Let's walk through how to implement a scope provider that restricts Ref references to only those Greeting instances marked as visible via Visible entries in the same model.


First, let's clarify the grammar with more descriptive names for better readability:

grammar org.example.MyDsl with org.eclipse.xtext.common.Terminals

generate myDsl "http://www.example.org/MyDsl"

Model:
	greetings+=Greeting+ (visibles+=Visible)* reference=Ref;

Ref:
	'ref_greeting' target=[Greeting];

Visible:
	'vis' visibleGreeting=[Greeting];

Greeting:
	'Hallo' name=ID '!';

Step 2: Implement the Custom Scope Provider

Create a scope provider class that overrides the default behavior for the Ref.target reference. This class will filter allowed Greeting instances based on the Visible entries in the same model:

package org.example.scoping;

import java.util.ArrayList;
import java.util.List;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EReference;
import org.eclipse.xtext.scoping.IScope;
import org.eclipse.xtext.scoping.Scopes;
import org.example.mydsl.myDsl.Model;
import org.example.mydsl.myDsl.Ref;
import org.example.mydsl.myDsl.Visible;

public class MyDslScopeProvider extends org.eclipse.xtext.scoping.impl.AbstractScopeProvider {

    @Override
    public IScope getScope(EObject context, EReference reference) {
        // Target only the Ref.target reference
        if (context instanceof Ref && reference.getName().equals("target")) {
            Model parentModel = (Model) context.eContainer();
            List<EObject> allowedGreetings = new ArrayList<>();

            if (!parentModel.getVisibles().isEmpty()) {
                // If there are Visible entries, only include referenced Greetings
                for (Visible visible : parentModel.getVisibles()) {
                    if (visible.getVisibleGreeting() != null) {
                        allowedGreetings.add(visible.getVisibleGreeting());
                    }
                }
            } else {
                // If no Visible entries, allow all Greetings in the model
                allowedGreetings.addAll(parentModel.getGreetings());
            }

            // Return a scope containing only allowed Greetings
            return Scopes.scopeFor(allowedGreetings);
        }
        // Fallback to default scope for all other references
        return super.getScope(context, reference);
    }
}

Step 3: Bind the Scope Provider in the Runtime Module

Update your runtime module to use the custom scope provider instead of the default:

package org.example;

import org.eclipse.xtext.scoping.IScopeProvider;
import org.example.scoping.MyDslScopeProvider;

public class MyDslRuntimeModule extends AbstractMyDslRuntimeModule {

    @Override
    public Class<? extends IScopeProvider> bindIScopeProvider() {
        return MyDslScopeProvider.class;
    }
}

Step 4: Add Optional Validation (For Clearer Error Messages)

To provide explicit error feedback when an invalid reference is used, add a validator class:

package org.example.validation;

import org.eclipse.xtext.validation.AbstractDeclarativeValidator;
import org.eclipse.xtext.validation.Check;
import org.example.mydsl.myDsl.Model;
import org.example.mydsl.myDsl.Ref;
import org.example.mydsl.myDsl.Visible;

public class MyDslValidator extends AbstractDeclarativeValidator {

    @Check
    public void checkRefTargetIsVisible(Ref ref) {
        Model parentModel = (Model) ref.eContainer();
        if (!parentModel.getVisibles().isEmpty() && ref.getTarget() != null) {
            boolean isVisible = false;
            for (Visible visible : parentModel.getVisibles()) {
                if (visible.getVisibleGreeting() != null && visible.getVisibleGreeting().equals(ref.getTarget())) {
                    isVisible = true;
                    break;
                }
            }
            if (!isVisible) {
                error("Greeting '" + ref.getTarget().getName() + "' is not marked as visible", 
                      ref, 
                      ref.eClass().getEStructuralFeature("target"));
            }
        }
    }
}

How It Works

  1. Scoping Behavior:
    • When no Visible entries exist, all Greeting instances in the model are allowed (matches your first input file's valid case).
    • When Visible entries are present, only the Greeting instances referenced in those entries appear in auto-completion and are valid targets.
  2. Validation:
    • Explicitly flags invalid references with a user-friendly error message, even if the user manually types an invalid name.

Testing Your Input Files

  • First Input (Valid):
    Hallo hallo! Hallo hallo_other_vis! ref_greeting hallo
    
    No Visible entries exist, so all greetings are allowed.
  • Second Input (Invalid):
    Hallo h! vis hallo_other_vis ref_greeting hallo_other_vis
    
    Two errors:
    1. hallo_other_vis doesn't exist in the model (invalid Visible reference).
    2. Even if it did exist, without a corresponding Greeting, the Ref target would be invalid.

内容的提问来源于stack exchange,提问作者Johan

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.25 07:47:14