Предположим следующую простую грамматику
(настоящая грамматика намного сложнее, и я хочу использовать неквалифицированные имена, чтобы различать guish различные типы ссылок, сохраняя простые "точечные" выражения для любого типа ссылок (исходная грамматика очень разрешительна на своем языке выражений).
Простой пример грамматики
Model: elements+=Element*;
Element: Entity | Field | Eval;
Entity:
"entity" name=ID "{" members+=Element* "}";
Field:
"field" name=ID ":" type=[Entity|ID];
Declaration:
Field | Entity
Eval: "eval" Expression;
Expression: DotExpr;
DotExpr returns Expression:
[Declaration|ID] ({DotExpr.head=current} '.' tail=[Declaration|ID])*;
Текст программы, написанный на эта грамматика может выглядеть так (включая пример того, что я хочу заставить работать):
Пример языкового содержимого
entity Orb {}
entity Foo {
entity Bar {
field orb : Orb
}
}
entity Foo {
entity Raboof {
field foo : Foo.Bar
// here for `foo` the scope of the referenced
// field `foo` needs to be computed
// (indirect scope of the entity `Bar` in `Foo`
// referenced by the referenced field `foo`)
eval foo.orb
// Here for `Foo` and `Bar` the default
// referencing mechanism would work
// (direct scope of the referenced entity `Bar` in `Foo`)
eval Foo.Bar.orb
}
}
// these should also both be valid and resolve accordingly:
eval Foo.Bar
eval Foo.Raboof
Простое определение области для DotExpr
, которое нарушает желаемое поведение
class ExampleScopeProvider extends AbstractExampleScopeProvider {
override getScope(EObject context, EReference reference) {
if (reference == ExamplePackage.eINSTANCE.dotExpr_Tail) {
val dotExpr = context.eContainer as DotExpr
val head = dotExpr.head
// if its an indirect reference:
if (head instanceof Field) {
return Scopes::scopeFor(head.type.members)
}
// otherwise just directly get the entities members
return Scopes::scopeFor(dotExpr.head.members)
}
return super.getScope(context, reference)
}
}
Вышеупомянутое разрешение области видимости будет работать отлично, когда есть уникальные имена, но как только я захочу разрешить что-то, как указано выше, где Foo
должен одновременно помещать члены обеих сущностей с именем Foo
в область видимости (Bar
и Raboof
) он, конечно, ломается.
То, что я думал до сих пор
С квалифицированными именами * 10 27 * Конечно, самый простой способ добиться желаемого поведения - разрешить QualifiedName
ссылку, тогда весь процесс будет работать из коробки без какой-либо необходимой дополнительной работы. Но это сломает желаемая функциональность с косвенными ссылками, также использующая точку (.
) для выбора. Я мог бы разрешить квалифицированные имена и изменить правило выбора с помощью другого грамматического элемента, такого как ->
, чтобы я имел для ссылки на косвенные ссылки через, например, foo->orb
, но я бы очень хотел, чтобы грамматика была как можно более простой и интуитивно понятной и избегала введения другого элемента syntacti c. Custom scoping Вероятно, решение заключается в каком-то способе настраиваемой области видимости, но я пока не мог этого понять. Возможно, можно объединить области действия всех объектов, которые выбраны левой стороной DotExpr
когда я хочу разрешить правую сторону. Но я не уверен, будет ли это наиболее эффективным решением, и особенно нетривиальным. Что-то вроде этого: class ExampleScopeProvider extends AbstractExampleScopeProvider {
override getScope(EObject context, EReference reference) {
if (reference == ExamplePackage.eINSTANCE.dotExpr_Tail) {
val containingDotExpr = context.eContainer as DotExpr
val parentScope = super.getScope(
containingDotExpr,
ExamplePackage.eINSTANCE.dotExpr_Head)
val membersOfAllParentScopeEntities =
parentScope.map(obj|obj.EObjectOrProxy.members).flatten
return Scopes::scopeFor(membersOfAllParentScopeEntities)
}
return super.getScope(context, reference)
}
}
Есть ли другой, возможно, канонический или распространенный способ чтобы добиться этого слияния областей видимости?