-module(greenwood@zipper).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/greenwood/zipper.gleam").
-export([zip/1, up/1, unzip/1, down_where/2, down/1, down_last_where/2, down_last/1, up_n/2, up_until/2, right_where/2, right/1, left_where/2, left/1, right_n/2, nth_child/2, left_n/2, right_n_where/3, left_n_where/3, right_until/3, left_until/3, right_n_until/4, left_n_until/4, find_descendant/2, set_focus/2, map_focus/2, insert_left/2, insert_right/2, insert_down/2, delete/1]).
-if(?OTP_RELEASE >= 27).
-define(MODULEDOC(Str), -moduledoc(Str)).
-define(DOC(Str), -doc(Str)).
-else.
-define(MODULEDOC(Str), -compile([])).
-define(DOC(Str), -compile([])).
-endif.
?MODULEDOC(
" Zipper (cursor) operations for Greenwood syntax trees.\n"
"\n"
" This module provides all navigation, mutation, and query operations for\n"
" the `Zipper` type. The zipper implements a Huet zipper providing a focused\n"
" view into the tree with O(1) local moves and edits.\n"
).
-file("src/greenwood/zipper.gleam", 17).
?DOC(
" Create a zipper focused on the root node.\n"
"\n"
" `cursor`\n"
).
-spec zip(greenwood:node_(EVB)) -> greenwood:zipper(EVB).
zip(Root) ->
{zipper, Root, []}.
-file("src/greenwood/zipper.gleam", 68).
?DOC(
" Move focus back up to the parent.\n"
"\n"
" `cursor`\n"
).
-spec up(greenwood:zipper(EVZ)) -> gleam@option:option(greenwood:zipper(EVZ)).
up(Zipper) ->
case erlang:element(3, Zipper) of
[] ->
none;
[Crumb | Rest] ->
Children = lists:append(
lists:reverse(erlang:element(4, Crumb)),
[{node_element, erlang:element(2, Zipper)} |
erlang:element(5, Crumb)]
),
Parent = {node,
erlang:element(2, Crumb),
Children,
erlang:element(3, Crumb)},
{some, {zipper, Parent, Rest}}
end.
-file("src/greenwood/zipper.gleam", 24).
?DOC(
" Reconstruct the full tree from a zipper by moving up to the root.\n"
"\n"
" `cursor`\n"
).
-spec unzip(greenwood:zipper(EVE)) -> greenwood:node_(EVE).
unzip(Zipper) ->
case up(Zipper) of
{some, Parent_zipper} ->
unzip(Parent_zipper);
none ->
erlang:element(2, Zipper)
end.
-file("src/greenwood/zipper.gleam", 630).
-spec always(greenwood:node_(any())) -> boolean().
always(_) ->
true.
-file("src/greenwood/zipper.gleam", 508).
-spec split_at_node(
list(greenwood:element(FAU)),
fun((greenwood:node_(FAU)) -> boolean()),
list(greenwood:element(FAU))
) -> gleam@option:option({list(greenwood:element(FAU)),
greenwood:node_(FAU),
list(greenwood:element(FAU))}).
split_at_node(Elements, Predicate, Left) ->
case Elements of
[] ->
none;
[{node_element, N} | Rest] ->
gleam@bool:guard(
Predicate(N),
{some, {Left, N, Rest}},
fun() ->
split_at_node(Rest, Predicate, [{node_element, N} | Left])
end
);
[Other | Rest@1] ->
split_at_node(Rest@1, Predicate, [Other | Left])
end.
-file("src/greenwood/zipper.gleam", 473).
-spec do_down(
greenwood:node_(FAE),
fun((greenwood:node_(FAE)) -> boolean()),
list(greenwood:crumb(FAE)),
list(greenwood:element(FAE))
) -> gleam@option:option(greenwood:zipper(FAE)).
do_down(Parent, Predicate, Crumbs, Left) ->
case split_at_node(erlang:element(3, Parent), Predicate, Left) of
{some, {New_left, Child, New_right}} ->
Crumb = {crumb,
erlang:element(2, Parent),
erlang:element(4, Parent),
New_left,
New_right},
{some, {zipper, Child, [Crumb | Crumbs]}};
none ->
none
end.
-file("src/greenwood/zipper.gleam", 41).
?DOC(
" Move focus to the first child Node matching a predicate.\n"
"\n"
" `cursor`\n"
).
-spec down_where(
greenwood:zipper(EVL),
fun((greenwood:node_(EVL)) -> boolean())
) -> gleam@option:option(greenwood:zipper(EVL)).
down_where(Zipper, Predicate) ->
do_down(erlang:element(2, Zipper), Predicate, erlang:element(3, Zipper), []).
-file("src/greenwood/zipper.gleam", 34).
?DOC(
" Move focus to the first child that is a Node.\n"
"\n"
" `cursor`\n"
).
-spec down(greenwood:zipper(EVH)) -> gleam@option:option(greenwood:zipper(EVH)).
down(Zipper) ->
down_where(Zipper, fun always/1).
-file("src/greenwood/zipper.gleam", 530).
-spec do_split_at_last_node(
list(greenwood:element(FBQ)),
fun((greenwood:node_(FBQ)) -> boolean()),
list(greenwood:element(FBQ)),
gleam@option:option({list(greenwood:element(FBQ)),
greenwood:node_(FBQ),
list(greenwood:element(FBQ))})
) -> gleam@option:option({list(greenwood:element(FBQ)),
greenwood:node_(FBQ),
list(greenwood:element(FBQ))}).
do_split_at_last_node(Elements, Predicate, Left, Best) ->
case Elements of
[] ->
Best;
[{node_element, N} | Rest] ->
case Predicate(N) of
true ->
do_split_at_last_node(
Rest,
Predicate,
[{node_element, N} | Left],
{some, {Left, N, Rest}}
);
false ->
do_split_at_last_node(
Rest,
Predicate,
[{node_element, N} | Left],
Best
)
end;
[Other | Rest@1] ->
do_split_at_last_node(Rest@1, Predicate, [Other | Left], Best)
end.
-file("src/greenwood/zipper.gleam", 523).
-spec split_at_last_node(
list(greenwood:element(FBG)),
fun((greenwood:node_(FBG)) -> boolean())
) -> gleam@option:option({list(greenwood:element(FBG)),
greenwood:node_(FBG),
list(greenwood:element(FBG))}).
split_at_last_node(Elements, Predicate) ->
do_split_at_last_node(Elements, Predicate, [], none).
-file("src/greenwood/zipper.gleam", 494).
-spec do_down_last(
greenwood:node_(FAN),
fun((greenwood:node_(FAN)) -> boolean()),
list(greenwood:crumb(FAN))
) -> gleam@option:option(greenwood:zipper(FAN)).
do_down_last(Parent, Predicate, Crumbs) ->
case split_at_last_node(erlang:element(3, Parent), Predicate) of
{some, {Left, Child, Right}} ->
Crumb = {crumb,
erlang:element(2, Parent),
erlang:element(4, Parent),
Left,
Right},
{some, {zipper, Child, [Crumb | Crumbs]}};
none ->
none
end.
-file("src/greenwood/zipper.gleam", 58).
?DOC(
" Move focus to the last child Node matching a predicate.\n"
"\n"
" `cursor`\n"
).
-spec down_last_where(
greenwood:zipper(EVU),
fun((greenwood:node_(EVU)) -> boolean())
) -> gleam@option:option(greenwood:zipper(EVU)).
down_last_where(Zipper, Predicate) ->
do_down_last(
erlang:element(2, Zipper),
Predicate,
erlang:element(3, Zipper)
).
-file("src/greenwood/zipper.gleam", 51).
?DOC(
" Move focus to the last child that is a Node.\n"
"\n"
" `cursor`\n"
).
-spec down_last(greenwood:zipper(EVQ)) -> gleam@option:option(greenwood:zipper(EVQ)).
down_last(Zipper) ->
down_last_where(Zipper, fun always/1).
-file("src/greenwood/zipper.gleam", 585).
-spec repeat_move(
greenwood:zipper(FDG),
integer(),
fun((greenwood:zipper(FDG)) -> gleam@option:option(greenwood:zipper(FDG)))
) -> gleam@option:option(greenwood:zipper(FDG)).
repeat_move(Zipper, N, Move) ->
case N of
_ when N < 0 ->
none;
0 ->
{some, Zipper};
_ ->
case Move(Zipper) of
{some, Z} ->
repeat_move(Z, N - 1, Move);
none ->
none
end
end.
-file("src/greenwood/zipper.gleam", 90).
?DOC(
" Move focus up `n` parents. Returns `None` if `n` is negative or would move\n"
" above the root.\n"
"\n"
" `up_n(z, by: 0)` returns `Some(z)`.\n"
"\n"
" `cursor`\n"
).
-spec up_n(greenwood:zipper(EWD), integer()) -> gleam@option:option(greenwood:zipper(EWD)).
up_n(Zipper, N) ->
repeat_move(Zipper, N, fun up/1).
-file("src/greenwood/zipper.gleam", 99).
?DOC(
" Ascend until the focused node's parent matches a predicate.\n"
" Returns the zipper focused on the first ancestor matching `predicate`,\n"
" or `None` if the root is reached without a match.\n"
"\n"
" `cursor`\n"
).
-spec up_until(greenwood:zipper(EWH), fun((greenwood:node_(EWH)) -> boolean())) -> gleam@option:option(greenwood:zipper(EWH)).
up_until(Zipper, Predicate) ->
case up(Zipper) of
none ->
none;
{some, Z} ->
gleam@bool:guard(
Predicate(erlang:element(2, Z)),
{some, Z},
fun() -> up_until(Z, Predicate) end
)
end.
-file("src/greenwood/zipper.gleam", 570).
-spec scan_right(
list(greenwood:element(FCU)),
fun((greenwood:node_(FCU)) -> boolean()),
list(greenwood:element(FCU))
) -> gleam@option:option({list(greenwood:element(FCU)),
greenwood:node_(FCU),
list(greenwood:element(FCU))}).
scan_right(Elements, Predicate, Left) ->
case Elements of
[] ->
none;
[{node_element, N} | Rest] ->
gleam@bool:guard(
Predicate(N),
{some, {Left, N, Rest}},
fun() ->
scan_right(Rest, Predicate, [{node_element, N} | Left])
end
);
[Other | Rest@1] ->
scan_right(Rest@1, Predicate, [Other | Left])
end.
-file("src/greenwood/zipper.gleam", 176).
?DOC(
" Move focus to the nearest sibling Node to the right matching a predicate.\n"
"\n"
" `cursor`\n"
).
-spec right_where(
greenwood:zipper(EXD),
fun((greenwood:node_(EXD)) -> boolean())
) -> gleam@option:option(greenwood:zipper(EXD)).
right_where(Zipper, Predicate) ->
case erlang:element(3, Zipper) of
[] ->
none;
[Crumb | Rest] ->
case scan_right(
erlang:element(5, Crumb),
Predicate,
[{node_element, erlang:element(2, Zipper)} |
erlang:element(4, Crumb)]
) of
{some, {New_left, New_focus, New_right}} ->
New_crumb = {crumb,
erlang:element(2, Crumb),
erlang:element(3, Crumb),
New_left,
New_right},
{some, {zipper, New_focus, [New_crumb | Rest]}};
none ->
none
end
end.
-file("src/greenwood/zipper.gleam", 169).
?DOC(
" Move focus to the nearest sibling Node to the right.\n"
"\n"
" `cursor`\n"
).
-spec right(greenwood:zipper(EWZ)) -> gleam@option:option(greenwood:zipper(EWZ)).
right(Zipper) ->
right_where(Zipper, fun always/1).
-file("src/greenwood/zipper.gleam", 555).
-spec scan_left(
list(greenwood:element(FCI)),
fun((greenwood:node_(FCI)) -> boolean()),
list(greenwood:element(FCI))
) -> gleam@option:option({list(greenwood:element(FCI)),
greenwood:node_(FCI),
list(greenwood:element(FCI))}).
scan_left(Elements, Predicate, Right) ->
case Elements of
[] ->
none;
[{node_element, N} | Rest] ->
gleam@bool:guard(
Predicate(N),
{some, {Rest, N, Right}},
fun() ->
scan_left(Rest, Predicate, [{node_element, N} | Right])
end
);
[Other | Rest@1] ->
scan_left(Rest@1, Predicate, [Other | Right])
end.
-file("src/greenwood/zipper.gleam", 138).
?DOC(
" Move focus to the nearest sibling Node to the left matching a predicate.\n"
"\n"
" `cursor`\n"
).
-spec left_where(
greenwood:zipper(EWU),
fun((greenwood:node_(EWU)) -> boolean())
) -> gleam@option:option(greenwood:zipper(EWU)).
left_where(Zipper, Predicate) ->
case erlang:element(3, Zipper) of
[] ->
none;
[Crumb | Rest] ->
case scan_left(
erlang:element(4, Crumb),
Predicate,
[{node_element, erlang:element(2, Zipper)} |
erlang:element(5, Crumb)]
) of
{some, {New_left, New_focus, New_right}} ->
New_crumb = {crumb,
erlang:element(2, Crumb),
erlang:element(3, Crumb),
New_left,
New_right},
{some, {zipper, New_focus, [New_crumb | Rest]}};
none ->
none
end
end.
-file("src/greenwood/zipper.gleam", 131).
?DOC(
" Move focus to the nearest sibling Node to the left.\n"
"\n"
" `cursor`\n"
).
-spec left(greenwood:zipper(EWQ)) -> gleam@option:option(greenwood:zipper(EWQ)).
left(Zipper) ->
left_where(Zipper, fun always/1).
-file("src/greenwood/zipper.gleam", 235).
?DOC(
" Move focus `n` sibling Nodes to the right. Negative `n` flips direction.\n"
"\n"
" `cursor`\n"
).
-spec right_n(greenwood:zipper(EXR), integer()) -> gleam@option:option(greenwood:zipper(EXR)).
right_n(Zipper, N) ->
gleam@bool:guard(
N < 0,
repeat_move(Zipper, - N, fun left/1),
fun() -> repeat_move(Zipper, N, fun right/1) end
).
-file("src/greenwood/zipper.gleam", 116).
?DOC(
" Descend to the nth child Node (1-indexed). Returns `None` if fewer than `n`\n"
" Node children exist.\n"
"\n"
" `cursor`\n"
).
-spec nth_child(greenwood:zipper(EWM), integer()) -> gleam@option:option(greenwood:zipper(EWM)).
nth_child(Zipper, N) ->
gleam@bool:guard(N < 1, none, fun() -> case down(Zipper) of
none ->
none;
{some, Z} when N =:= 1 ->
{some, Z};
{some, Z@1} ->
right_n(Z@1, N - 1)
end end).
-file("src/greenwood/zipper.gleam", 207).
?DOC(
" Move focus `n` sibling Nodes to the left. Negative `n` flips direction.\n"
"\n"
" `cursor`\n"
).
-spec left_n(greenwood:zipper(EXI), integer()) -> gleam@option:option(greenwood:zipper(EXI)).
left_n(Zipper, N) ->
gleam@bool:guard(
N < 0,
repeat_move(Zipper, - N, fun right/1),
fun() -> repeat_move(Zipper, N, fun left/1) end
).
-file("src/greenwood/zipper.gleam", 244).
?DOC(
" Move focus `n` sibling Nodes to the right matching a predicate.\n"
" Negative `n` flips direction.\n"
"\n"
" `cursor`\n"
).
-spec right_n_where(
greenwood:zipper(EXV),
integer(),
fun((greenwood:node_(EXV)) -> boolean())
) -> gleam@option:option(greenwood:zipper(EXV)).
right_n_where(Zipper, N, Predicate) ->
case N of
0 ->
{some, Zipper};
_ when N < 0 ->
left_n_where(Zipper, - N, Predicate);
_ ->
case right_where(Zipper, Predicate) of
{some, Z} ->
right_n_where(Z, N - 1, Predicate);
none ->
none
end
end.
-file("src/greenwood/zipper.gleam", 216).
?DOC(
" Move focus `n` sibling Nodes to the left matching a predicate.\n"
" Negative `n` flips direction.\n"
"\n"
" `cursor`\n"
).
-spec left_n_where(
greenwood:zipper(EXM),
integer(),
fun((greenwood:node_(EXM)) -> boolean())
) -> gleam@option:option(greenwood:zipper(EXM)).
left_n_where(Zipper, N, Predicate) ->
case N of
0 ->
{some, Zipper};
_ when N < 0 ->
right_n_where(Zipper, - N, Predicate);
_ ->
case left_where(Zipper, Predicate) of
{some, Z} ->
left_n_where(Z, N - 1, Predicate);
none ->
none
end
end.
-file("src/greenwood/zipper.gleam", 264).
?DOC(
" Scan right siblings for `target`, aborting early if `stop` matches first.\n"
" Returns `None` if siblings are exhausted or `stop` fires.\n"
"\n"
" `cursor`\n"
).
-spec right_until(
greenwood:zipper(EYA),
fun((greenwood:node_(EYA)) -> boolean()),
fun((greenwood:node_(EYA)) -> boolean())
) -> gleam@option:option(greenwood:zipper(EYA)).
right_until(Zipper, Target, Stop) ->
case right(Zipper) of
none ->
none;
{some, Next} ->
gleam@bool:guard(
Stop(erlang:element(2, Next)),
none,
fun() ->
gleam@bool:guard(
Target(erlang:element(2, Next)),
{some, Next},
fun() -> right_until(Next, Target, Stop) end
)
end
)
end.
-file("src/greenwood/zipper.gleam", 282).
?DOC(
" Scan left siblings for `target`, aborting early if `stop` matches first.\n"
"\n"
" `cursor`\n"
).
-spec left_until(
greenwood:zipper(EYG),
fun((greenwood:node_(EYG)) -> boolean()),
fun((greenwood:node_(EYG)) -> boolean())
) -> gleam@option:option(greenwood:zipper(EYG)).
left_until(Zipper, Target, Stop) ->
case left(Zipper) of
none ->
none;
{some, Next} ->
gleam@bool:guard(
Stop(erlang:element(2, Next)),
none,
fun() ->
gleam@bool:guard(
Target(erlang:element(2, Next)),
{some, Next},
fun() -> left_until(Next, Target, Stop) end
)
end
)
end.
-file("src/greenwood/zipper.gleam", 301).
?DOC(
" Scan `n` target-matching right siblings, stopping early if `stop` fires.\n"
" `right_n_until(zipper:, n: 0, ..)` returns `Some(zipper)`.\n"
"\n"
" `cursor`\n"
).
-spec right_n_until(
greenwood:zipper(EYM),
integer(),
fun((greenwood:node_(EYM)) -> boolean()),
fun((greenwood:node_(EYM)) -> boolean())
) -> gleam@option:option(greenwood:zipper(EYM)).
right_n_until(Zipper, N, Target, Stop) ->
case N of
0 ->
{some, Zipper};
_ ->
case right_until(Zipper, Target, Stop) of
none ->
none;
{some, Next} ->
right_n_until(Next, N - 1, Target, Stop)
end
end.
-file("src/greenwood/zipper.gleam", 321).
?DOC(
" Scan `n` target-matching left siblings, stopping early if `stop` fires.\n"
" `left_n_until(zipper:, n: 0, ..)` returns `Some(zipper)`.\n"
"\n"
" `cursor`\n"
).
-spec left_n_until(
greenwood:zipper(EYS),
integer(),
fun((greenwood:node_(EYS)) -> boolean()),
fun((greenwood:node_(EYS)) -> boolean())
) -> gleam@option:option(greenwood:zipper(EYS)).
left_n_until(Zipper, N, Target, Stop) ->
case N of
0 ->
{some, Zipper};
_ ->
case left_until(Zipper, Target, Stop) of
none ->
none;
{some, Next} ->
left_n_until(Next, N - 1, Target, Stop)
end
end.
-file("src/greenwood/zipper.gleam", 612).
-spec search_siblings_and_below(
greenwood:zipper(FDS),
fun((greenwood:node_(FDS)) -> boolean())
) -> gleam@option:option(greenwood:zipper(FDS)).
search_siblings_and_below(Zipper, Predicate) ->
gleam@bool:guard(
Predicate(erlang:element(2, Zipper)),
{some, Zipper},
fun() -> case do_find_descendant(Zipper, Predicate) of
{some, _} = Found ->
Found;
none ->
case right(Zipper) of
none ->
none;
{some, Next} ->
search_siblings_and_below(Next, Predicate)
end
end end
).
-file("src/greenwood/zipper.gleam", 601).
-spec do_find_descendant(
greenwood:zipper(FDN),
fun((greenwood:node_(FDN)) -> boolean())
) -> gleam@option:option(greenwood:zipper(FDN)).
do_find_descendant(Zipper, Predicate) ->
case down(Zipper) of
none ->
none;
{some, Child_z} ->
search_siblings_and_below(Child_z, Predicate)
end.
-file("src/greenwood/zipper.gleam", 341).
?DOC(
" Depth-first search below the focus for a descendant matching a predicate.\n"
" Returns a zipper focused on the match, or `None`.\n"
"\n"
" `cursor`\n"
).
-spec find_descendant(
greenwood:zipper(EYY),
fun((greenwood:node_(EYY)) -> boolean())
) -> gleam@option:option(greenwood:zipper(EYY)).
find_descendant(Zipper, Predicate) ->
do_find_descendant(Zipper, Predicate).
-file("src/greenwood/zipper.gleam", 351).
?DOC(
" Replace the focused node.\n"
"\n"
" `cursor`\n"
).
-spec set_focus(greenwood:zipper(EZD), greenwood:node_(EZD)) -> greenwood:zipper(EZD).
set_focus(Zipper, Node) ->
{zipper, Node, erlang:element(3, Zipper)}.
-file("src/greenwood/zipper.gleam", 361).
?DOC(
" Apply a transform to the focused node.\n"
"\n"
" `cursor`\n"
).
-spec map_focus(
greenwood:zipper(EZH),
fun((greenwood:node_(EZH)) -> greenwood:node_(EZH))
) -> greenwood:zipper(EZH).
map_focus(Zipper, F) ->
{zipper, F(erlang:element(2, Zipper)), erlang:element(3, Zipper)}.
-file("src/greenwood/zipper.gleam", 372).
?DOC(
" Insert an element to the left of the focus. Focus does not move.\n"
" Returns `None` if the focus is the root (no parent to hold the sibling).\n"
"\n"
" `cursor`\n"
).
-spec insert_left(greenwood:zipper(EZM), greenwood:element(EZM)) -> gleam@option:option(greenwood:zipper(EZM)).
insert_left(Zipper, Element) ->
case erlang:element(3, Zipper) of
[] ->
none;
[Crumb | Rest] ->
New_crumb = {crumb,
erlang:element(2, Crumb),
erlang:element(3, Crumb),
[Element | erlang:element(4, Crumb)],
erlang:element(5, Crumb)},
{some, {zipper, erlang:element(2, Zipper), [New_crumb | Rest]}}
end.
-file("src/greenwood/zipper.gleam", 389).
?DOC(
" Insert an element to the right of the focus. Focus does not move.\n"
" Returns `None` if the focus is the root.\n"
"\n"
" `cursor`\n"
).
-spec insert_right(greenwood:zipper(EZR), greenwood:element(EZR)) -> gleam@option:option(greenwood:zipper(EZR)).
insert_right(Zipper, Element) ->
case erlang:element(3, Zipper) of
[] ->
none;
[Crumb | Rest] ->
New_crumb = {crumb,
erlang:element(2, Crumb),
erlang:element(3, Crumb),
erlang:element(4, Crumb),
[Element | erlang:element(5, Crumb)]},
{some, {zipper, erlang:element(2, Zipper), [New_crumb | Rest]}}
end.
-file("src/greenwood/zipper.gleam", 407).
?DOC(
" Insert a child as the first child of the focus and move focus to it.\n"
" Returns `None` if the focus has no children list (impossible for Node, but\n"
" kept as Option for API consistency).\n"
"\n"
" `cursor`\n"
).
-spec insert_down(greenwood:zipper(EZW), greenwood:node_(EZW)) -> greenwood:zipper(EZW).
insert_down(Zipper, Child) ->
Crumb = {crumb,
erlang:element(2, erlang:element(2, Zipper)),
erlang:element(4, erlang:element(2, Zipper)),
[],
erlang:element(3, erlang:element(2, Zipper))},
{zipper, Child, [Crumb | erlang:element(3, Zipper)]}.
-file("src/greenwood/zipper.gleam", 429).
?DOC(
" Delete the focused node. Focus shifts to:\n"
" 1. Right sibling (if exists)\n"
" 2. Left sibling (if exists)\n"
" 3. Parent with focus removed from children\n"
"\n"
" Returns `None` if the focus is the root.\n"
"\n"
" `cursor`\n"
).
-spec delete(greenwood:zipper(FAA)) -> gleam@option:option(greenwood:zipper(FAA)).
delete(Zipper) ->
case erlang:element(3, Zipper) of
[] ->
none;
[Crumb | Rest] ->
case scan_right(
erlang:element(5, Crumb),
fun always/1,
erlang:element(4, Crumb)
) of
{some, {New_left, New_focus, New_right}} ->
New_crumb = {crumb,
erlang:element(2, Crumb),
erlang:element(3, Crumb),
New_left,
New_right},
{some, {zipper, New_focus, [New_crumb | Rest]}};
none ->
case scan_left(
erlang:element(4, Crumb),
fun always/1,
erlang:element(5, Crumb)
) of
{some, {New_left@1, New_focus@1, New_right@1}} ->
New_crumb@1 = {crumb,
erlang:element(2, Crumb),
erlang:element(3, Crumb),
New_left@1,
New_right@1},
{some, {zipper, New_focus@1, [New_crumb@1 | Rest]}};
none ->
Children = lists:append(
lists:reverse(erlang:element(4, Crumb)),
erlang:element(5, Crumb)
),
Parent = {node,
erlang:element(2, Crumb),
Children,
erlang:element(3, Crumb)},
{some, {zipper, Parent, Rest}}
end
end
end.