Xtext Scopes in the SADL IDE

Last revised 1/6/2022. Contact us.

Introduction

Scoping defines the visibility rules inside of a SADL file and across SADL file boundaries. For example, if a class C1 is defined in a SADL file with namespace (URI) http://sadl.org/ns1, the class C1 is visible anywhere in that SADL file and in any model that imports http://sadl.org/ns1. On the other hand, if a variable X is defined in a rule, query, or test in http://sadl.org/ns1, X is visible only within that rule, query, or test. If there is a variable X in another rule, query, or test in http://sadl.org/ns1, that X is entirely independent and in its turn visible only within its rule, query, or test.

Furthermore, scoping must differentiate the location of the definition of a class, property, instance, or variable from references to it. Consider this small model.

The instance i2 appears in the statements on lines 5 and 6. Even though the statement on line 5 appears first, it is the statement on line 6 that defines i2. Line 5 contains only a reference to i2. In order for the hyperlinking to definition and to references to work properly, scoping must correctly identify which occurrence is the definition and which occurrences are references.

Xtext scoping is a complex topic. Some references that may prove useful include the following.

Into the Caldron

A scope describes the visible names, which depends on the context, and maps names to the parse tree's EMF EObjects in a chain of scopes. Local scopes have to do with references within a Resource (SADL file). Global scopes have to do with those names that are visible from outside a Resource (SADL or OWL file), assuming that the Resource is imported.

The IScope class represents an element of a linked list of scopes. A scope can be nested within an outer scope. Each scope is like a symbol table or map where the keys are strings and the values are IEObjectDescription instances, abstract descriptions of real EObject instances.

In the SADL IDE, the following classes deal with scoping.

Of particular interest to this discussion is the class SADLScopeProvider. this class contains a set of methods named localScope_01, localScope_02, ..., localScope06. These are the scope providers referred to below.

Scope processing in SADL has a nested iterative structure. The outer iteration is over the local scope providers, methods in SADLScopeProvider named localScope_01, localScope_02, ..., localScope06. The inner iteration is over the contents of the parse tree. Written as pseudo code, the overall outline of the scope processing looks something like this.

 for (scopeProvider : scopeProviders) {

 for (eObj : resource.allContents) {

//  Check if this eObj's characteristics are matched by the conditions of the current scopeProvider and if so create a scope for the eObj if it is a SadlResource.
//  This occurrence of eObj will be the definition of the identified SadlResource. All other SadlResources with the same URI will be references.
//  Note that the conditions of the scope providers can only match (return true) for an EObject which is a SadlResource.

}

}

In other words, each local scope provider checks for definitions. The scope providers are processed from highest priority statements that are definitions to lowest priority statements that might be definitions. For each local scope provider, go through the parse tree's EObjects and identify the definitions, then create scoping information for each one that qualifies.

Note: within the JenaBasedSadlModelProcessor, the method getNewVar uses an IScopeProvider to help determine whether to use an existing variable in the local scope or create a new one.

Logic of Local Scope Providers by Level

The logic for each of the six levels is described below. (The code in SADLScopeProvider is the ultimate truth; this is a snapshot for documentation purposes.)

  1. returns true for a SadlResource AND
    1. (eContainer instanceOf SadlClassOrPropertyDeclaration AND eContainingFeature == SADL_CLASS_OR_PROPERTY_DECLARATION__CLASS_OR_PROPERTY AND eContainer.eContainer NOT instanceof SadlDifferentFrom)
    2. OR
    3. (eContainer instanceof SadProperty AND eContainer.isPrimaryDeclaration() AND eContainingFeature == SADL_ROPERTY__NAME_OR_REF)
  2. returns true for a SadlResource AND
    1. (eContainer instanceof SadlProperty AND eContainingFeature == SADL_PROPERTY__NAME_OR_REF)
    2. OR
    3. (eContainer instanceof SadlProperty AND eContainingFeature == SADL_PROPERTY__NAME_DECLARATION)
  3. returns true for a SadlResource AND
    1. (eContainer instanceof EquationStatement &&(eContainer as EquationStatement).name.equals(the SadlResource))
    2. OR
    3. (eContainer instanceof ExternalEquationStatement &&(eContainer as ExternalEquationStatement).name.equals(the SadlResource))
    4. OR
    5. eContainer instanceof SadlNecessaryAndSufficient AND  eContainingFeature == SADL_NECESSARY_AND_SUFFICIENT__OBJECT)
  4. returns true for a SadlResource  AND
    1. eContainer instanceof SadlNestedInstance AND  eContainingFeature == SADL_INSTANCE__INSTANCE
  5. returns true for a SadlResource AND
    1. eContainer instanceof SadlInstance AND  eContainingFeature == SADL_INSTANCE__NAME_OR_REF AND eC.type != null // last condition is GH-511 addition
  6. returns true for a SadlResource AND
    1. (eContainer instanceof SadlMustBeOneOf  AND  eContainingFeature == SADL_MUST_BE_ONE_OF__VALUES)
    2. OR
    3. (eContainer instanceof SadlCanOnlyBeOneOf and  eContainingFeature == SADL_CAN_ONLY_BE_ONE_OF__VALUES)

Examples

All of the scoping providers only apply to SadlResource as that is the only EObject that is linked. The matches to scoping providers by SadlResource are:

  1. Cls in the first statement matches for localScope_01
  2. objProp in the first statement matches for localScope_02
  3. i1 in the second statement matches for localScope_05
  4. i2 in the fourth statement matches for localScope_05