xtext: эффективный способ пользовательской области видимости

Я попытался настроить область видимости следующим образом:

В файле MyDslScopeProvider, расширяющем AbstractMyDslScopeProvider, я реализовал функцию с такой сигнатурой:

override def IScope getScope(EObject context, EReference reference)

и я использовал такие случаи

if (reference == SpectraPackage.Literals.SOMETHING__POINTER)

но у меня есть функция в моей грамматике, у которой есть параметры, и мы можем объявить их внутри локальных переменных. Я не хочу, чтобы эти локальные переменные и параметры этой функции были видны снаружи, я хочу, чтобы они были видны только внутри функции, поэтому я сделал что-то вроде этого:

      if (contextDecl instanceof function) {

                val fun= contextDecl as function
                val allContentsCurrFile = EcoreUtil2.getAllContentsOfType(fun,Constant)
                EObjectsInScope.addAll(fun.params)
                EObjectsInScope.addAll(allContentsCurrFile)
                return Scopes.scopeFor(EObjectsInScope)

            }
else{
                val removeEobjects = newArrayList()
                EObjectsInScope.addAll(EcoreUtil2.getAllContentsOfType(root,EObject))
                val funList= EcoreUtil2.getAllContentsOfType(root,function) as List<function>
                    for(function f: funList){
                        removeEobjects.addAll(f.varDeclList)
                        removeEobjects.addAll(f.params.params)
                        removeEobjects.addAll(EcoreUtil2.getAllContentsOfType(f,Constant))
                    }

                EObjectsInScope.removeAll(removeEobjects)

                return Scopes.scopeFor(EObjectsInScope)

Это очень неэффективно, чтобы получить все EObjects и удалить переменные, которые я не хочу видеть снаружи (это занимает много времени). Есть ли способ сделать это более эффективно? Спасибо.


person RoG    schedule 26.09.2016    source источник
comment
Не могли бы вы поделиться полным кодом? Я не понимаю, как элементы, которые вы хотите удалить, вообще оказались в «EObjectsInScope».   -  person Sven Efftinge    schedule 26.09.2016


Ответы (1)


EDIT: вас также может заинтересовать мой ответ на "Xtext: IResourceScopeCache для предотвращения дорогостоящего расчета области"

Прежде всего, если вы говорите о локальных переменных, вы, вероятно, не хотите разрешать использование локальных переменных до их объявления, например.

function foo() {
  x = x + 1;
  int x = 0;
}

Таким образом, вы на самом деле делаете слишком много работы, используя getAllContentsOfType().

Чего именно вы пытаетесь достичь с помощью своих оптимизаций? Лучшая производительность для поддержки контента внутри функции? Лучшая скорость для большого количества моделей с небольшими функциями? Лучшая скорость для многих крупных функций?

Имейте в виду, чтобы избежать преждевременной оптимизации — более важно поддерживать ваш код в сопровождении и оптимизировать для скорости, только если он не масштабируется до рабочих нагрузок, которые вам действительно нужно обрабатывать. Использовали ли вы профайлер для поиска горячих точек? Человеческая интуиция может сильно ошибаться, когда дело доходит до узких мест в производительности.

В любом случае, если предположить, что вам нужно улучшить скорость определения области, но у вас нет больших рабочих нагрузок, в качестве первого шага я бы предложил использовать TreeIterator для обхода тела функции, собирая локальные переменные, которые должны быть видны, и, используя возвращаемое значение EcoreUtil2.getAllContainers(context) в качестве руководства, когда использовать prune() и когда использовать next().

I.e.

import static extension org.eclipse.xtext.EcoreUtil2.*
// ...

val predecessors = new ArrayList<EObject>

val iterator = EcoreUtils.getAllContents(function, true)
// Could be optimized further
val descentGuide = context.allContainers.dropWhile[it != function].toList.reverseView.iterator

var current = iterator.next
var nextDescent = descentGuide.next
while(current != context) {
  // collect list with local variables here
  predecessors += current
  if(current == nextDescent) {
      // Reached another ancestor of context - will look for the following ancestor next
      nextDescent = descentGuide.next
  } else {
      iterator.prune
  }
  current = iterator.next
}

// Reverse so innermost declarations shadow outer declarations
val localVariables = predecessors.filter(LocalVariableDeclaration).toList.reverseView

Я не компилировал/тестировал код, но надеюсь, что идея понятна.

Цикл while должен заканчиваться в конце, потому что в какой-то момент будет достигнуто context, но для большей надежности может иметь смысл добавить && iterator.hasNext в цикл while.

person Bernhard Stadler    schedule 28.09.2016