Skip to content

Commit

Permalink
add RecursivePred
Browse files Browse the repository at this point in the history
  • Loading branch information
aplavin committed Dec 18, 2024
1 parent 1c3f128 commit 0f493ef
Show file tree
Hide file tree
Showing 4 changed files with 67 additions and 2 deletions.
2 changes: 1 addition & 1 deletion Project.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
name = "AccessorsExtra"
uuid = "33016aad-b69d-45be-9359-82a41f556fd4"
authors = ["Alexander Plavin <[email protected]>"]
version = "0.1.86"
version = "0.1.87"

[deps]
Accessors = "7d9f7c33-5ae7-4f3b-8dc6-eff91059b697"
Expand Down
2 changes: 1 addition & 1 deletion src/AccessorsExtra.jl
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ export
@replace, @push, @pushfirst, @pop, @popfirst,
@getall, @setall,
construct, @construct,
RecursiveOfType,
RecursiveOfType, RecursivePred,
keyed, enumerated, selfcontext, stripcontext, hascontext,
maybe, osomething, oget, hasoptic, @maybe, @oget, @osomething,
modifying, onget, onset, ongetset,
Expand Down
59 changes: 59 additions & 0 deletions src/recursive.jl
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,65 @@ struct Children end
@inline setall(obj, c::Children, vals) = setall(obj, _chooseoptic_byval(obj, c), vals)


# lots of duplication: RecursivePred vs RecursiveOfType, getall vs modify, etc
# could in principle combine them, but Julia optimization heuristics are prohibitive to this kind of recursive functions

struct RecursivePred{OT,RT,ORD,O}
outpred::OT
recpred::RT
order::ORD
optic::O
end
Broadcast.broadcastable(o::RecursivePred) = Ref(o)
function RecursivePred(out, optic=Children(); recurse=Returns(true), order=nothing)
_check_order(order)
RecursivePred(out, recurse, Val(order), optic)
end
OpticStyle(::Type{<:RecursivePred}) = ModifyBased()

function modify(f, obj, or::RecursivePred, objs...)
recurse(o, bs...) = _walk_modify(var"#self#", f, o, or, bs...)
_walk_modify(recurse, f, obj, or, objs...)
end
_walk_modify(recurse, f, obj, or::RecursivePred{<:Any,<:Any,Val{ORD}}, objs...) where {ORD} =
if or.outpred(obj)
if ORD === nothing || !or.recpred(obj)
f(obj, objs...)
elseif ORD === :pre
modify(recurse, f(obj, objs...), or.optic, objs...)
elseif ORD === :post
f(modify(recurse, obj, or.optic, objs...), objs...)
else
error("Unknown order: $ORD")
end
elseif or.recpred(obj)
modify(recurse, obj, or.optic, objs...)
else
obj
end

function getall(obj, or::RecursivePred)
recurse(o) = _walk_getall(var"#self#", o, or)
_walk_getall(recurse, obj, or)
end
_walk_getall(recurse, obj, or::RecursivePred{<:Any,<:Any,Val{ORD}}) where {ORD} =
if or.outpred(obj)
if ORD === nothing || !or.recpred(obj)
return (obj,)
elseif ORD === :pre
return (obj, _getall(recurse, obj, or.optic)...)
elseif ORD === :post
return (_getall(recurse, obj, or.optic)..., obj)
else
error("Unknown order: $ORD")
end
elseif or.recpred(obj)
_getall(recurse, obj, or.optic)
else
()
end


""" RecursiveOfType(out::Type, [optic=Children()]; [recurse::Type=Any])
Optic that references all values of type `out` located arbitrarily deep.
Expand Down
6 changes: 6 additions & 0 deletions test/recursive.jl
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
@testitem "RecursivePred" begin
m = (a=1, bs=((c=1, d="2"), (c=3, d="xxx")))
@test (@getall m |> RecursivePred(x -> x isa Number)) == (1, 1, 3)
@test (@getall m |> RecursivePred(x -> x isa Number && x < 2)) == (1, 1)
end

@testitem "basic" begin
using StaticArrays

Expand Down

0 comments on commit 0f493ef

Please sign in to comment.