Skip to content

Commit

Permalink
Add first support for "closure"
Browse files Browse the repository at this point in the history
  • Loading branch information
aranega committed Sep 18, 2024
1 parent 8eba89a commit f374770
Show file tree
Hide file tree
Showing 3 changed files with 47 additions and 11 deletions.
8 changes: 8 additions & 0 deletions pyecoreocl/compiler.py
Original file line number Diff line number Diff line change
Expand Up @@ -606,6 +606,14 @@ def rule_index_of(emitter, ctx):
emitter.visit(ctx.argExp().body[0])
emitter.inline(")")

@call_rule
def rule_closure(emitter, ctx):
emitter.inline("ocl.closure(")
emitter.visit(ctx.expression)
emitter.inline(", ")
emitter.visit(ctx.argExp())
emitter.inline(")")


def default_collection_call(emitter, ctx):
operation = ctx.attname.text
Expand Down
28 changes: 27 additions & 1 deletion pyecoreocl/runtime.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ def is_unique(lst):


def geta(o, attr):
if isinstance(o, Iterable):
if isinstance(o, Iterable) and not isinstance(o, (str, bytes)):
return (getattr(e, attr) for e in o)
return getattr(o, attr)

Expand All @@ -25,3 +25,29 @@ def flatten(items):
yield sub_x
else:
yield x


def closure(source, body):
def recurse(source, acc):
for element in source:
if element in acc:
yield from acc
else:
result = body(element)
if result is None:
yield from acc
elif isinstance(result, Iterable):
acc.extend(r for r in result if r not in acc)
yield from acc
yield from (
r for r in flatten(recurse(result, acc)) if r not in acc
)
else:
if result not in acc:
acc.append(result)
yield from acc
yield from (
r for r in flatten(recurse([result], acc)) if r not in acc
)

return recurse(source, [])
22 changes: 12 additions & 10 deletions tests/test_collection_lib.py
Original file line number Diff line number Diff line change
Expand Up @@ -193,13 +193,15 @@ def test__index_of():
assert !Sequence{1, 2}->collect(e | e + 1)->indexOf(2)! == 0


# anonSources->iterate(iterator : T; anonAcc : Result(T) = anonInit |
# if anonAcc->includes(iterator)
# then anonAcc
# else let anonBody : OclAny = body in
# let anonResults : Result(T) = anonAcc->add(iterator) in
# if anonBody.oclIsKindOf(CollectionType)
# then anonRecurse(anonBody.oclAsType(Collection(T)), anonResults)
# else anonRecurse(anonBody.oclAsType(T)->asSet(), anonResults)
# endif
# endif)


def test__closure():
@dataclass
class A:
children: list["A"] | None

x1, x2 = A(children=[]), A(children=None)
a = A(children=[x1, x2])

print(!Sequence{a}->closure(e | e.children)->asSequence()!)
assert !Sequence{a}->closure(e | e.children)->asSequence()! == [x1, x2]

0 comments on commit f374770

Please sign in to comment.