parens work... moving on to documenting work

This commit is contained in:
2026-06-03 15:17:55 -07:00
parent 4f4adaa284
commit 4e54bebeba
3 changed files with 241 additions and 175 deletions
+62 -98
View File
@@ -1,15 +1,15 @@
-module(gsc_ntree).
-export_type([
ntree/2,
ntree/0
ntree/2, ntree/0,
nforest/2, nforest/0,
nt/2, nt/0,
nf/2, nf/0
]).
-export([
nstem/2,
flatten/1,
deleaf0/1,
releaf0/2
nstem/2, meta/1, kids/1,
flatten_tree/1, flatten_forest/1
]).
@@ -19,16 +19,32 @@
%% API: types
%%=====================================================
-record(ns, {val :: any(), kids :: list(any())}).
-record(nl, {val :: any()}).
% @doc stem record
-record(ns, {meta :: any(),
kids :: list(any())}).
%% @doc ntree(S, L) is a "node tree" (meaning stems
%% have values and children)
-type ntree(S, L)
:: #ns{val :: S, kids :: [ntree(S, L)]}
| #nl{val :: L}.
% @doc `ntree(S, L)' is a "node tree" (meaning stems
% have values and children)
%
% for the purposes of the compiler, the key observation
% is that a flat list of tokens is already a forest
-type ntree(S, L) :: #ns{meta :: S, kids :: [ntree(S, L)]}
| L.
-type ntree() :: ntree(any(), any()).
% @doc forest is just a list of trees
-type nforest(S, L) :: [ntree(S, L)].
% aliases
-type nt(S, L) :: ntree(S, L).
-type nf(S, L) :: nforest(S, L).
-type ntree() :: ntree(any(), any()).
-type nforest() :: [ntree()].
-type nt() :: ntree().
-type nf() :: nforest().
%%=====================================================
@@ -36,92 +52,40 @@
%%=====================================================
-spec nstem(Root, List) -> Tree when
Root :: X,
List :: list(Y),
Tree :: ntree(X, Y),
X :: any(),
Y :: any().
% @doc
% You *probably* want `releaf0/2' instead.
%
% This function naively wraps each element in the list
% in a leaf type, even if it's already wrapped.
%
% nstem(root, [Foo, Bar, Baz]) ~>
% *s root
% |
% +--- .l Foo
% |
% +--- .l Bar
% |
% +--- .l Baz
%
% Much more common use case is to releaf only the input
% nodes which are not already wrapped, which is what
% `releaf0/2' does.
% @end
nstem(Root, List) ->
{ns, Root, [{nl, Y} || Y <- List]}.
-spec flatten(Tree) -> LeafVals when
Tree :: ntree(any(), LeafType),
LeafVals :: [LeafType],
LeafType :: any().
flatten({nl, X}) ->
[X];
flatten({ns, _, Keeids}) ->
lists:flatten([flatten(Keeid) || Keeid <- Keeids]).
-spec deleaf0(Tree) -> Result when
Tree :: ntree(S, L),
Result :: [L | Tree],
S :: any(),
L :: any().
% @doc unwrap the leaf children, and leave the stem
% children intact
%
% ex. 1:
% (+ 1 2 (* 3 4) 5)
% ~> '(1 2 (* 3 4) 5)
%
% ex. 2:
% {ns, '+', [{nl, 1},
% {nl, 2},
% {ns, '*', [{nl, 3}, {nl, 4}]},
% {nl, 5}]}
% ~> [1, 2, {ns, '*', [{nl, 3}, {nl, 4}]}, 5]
% @end
deleaf0({nl, L}) -> [L];
deleaf0({ns, _, Ls}) -> dl0([], Ls).
dl0(Stk, []) -> lists:reverse(Stk);
dl0(Stk, [{nl, X} | Rest]) -> dl0([X | Stk], Rest);
dl0(Stk, [X | Rest]) -> dl0([X | Stk], Rest).
-spec releaf0(Root, Keeids) -> Rooted when
-spec nstem(Root, Forest) -> Tree when
Root :: S,
Keeids :: [L | ntree(S, L)],
Rooted :: ntree(S, L),
Forest :: nforest(S, L),
Tree :: ntree(S, L),
S :: any(),
L :: any().
% @doc notional inverse of `deleaf0/1'
%
% Note that this does **NOT** double-wrap leafs in the
% input
releaf0(Root, Ks) ->
#ns{val = Root,
kids = lists:map(fun rl0/1, Ks)}.
nstem(Root, List) ->
{ns, Root, List}.
rl0(X = #ns{}) -> X;
rl0(X = #nl{}) -> X;
rl0(X) -> {nl, X}.
meta(#ns{meta = M}) -> M.
kids(#ns{kids = K}) -> K.
-spec flatten_tree(Tree) -> Leafs when
Tree :: ntree(_, L),
Leafs :: [L],
L :: any().
flatten_tree(T) ->
lists:flatten(ft(T)).
-spec flatten_forest(Forest) -> Leafs when
Forest :: nforest(_, L),
Leafs :: [L],
L :: any().
flatten_forest(F) ->
lists:flatten(ff(F)).
ft(#ns{kids = F}) -> ff(F);
ft(Leaf) -> [Leaf].
ff(F) -> [ft(T) || T <- F].