parens work... moving on to documenting work
This commit is contained in:
@@ -0,0 +1,67 @@
|
|||||||
|
-spec mktree(Signal) -> Tree when
|
||||||
|
Signal :: gsc:signal(),
|
||||||
|
Tree :: gsc_ntree:ntree().
|
||||||
|
|
||||||
|
% @doc make into a tree
|
||||||
|
mktree(Sig) ->
|
||||||
|
Tree0 = gsc_ntree:nstem(vtokens, Sig),
|
||||||
|
Tree1 = rerootl_tkstr("=>", Tree0),
|
||||||
|
Tree2 = rerootl_tkstr("*", Tree1),
|
||||||
|
Tree2.
|
||||||
|
|
||||||
|
|
||||||
|
rerootl_tkstr(S, Tree0 = #ns{val = Root0}) ->
|
||||||
|
Kids0 = gsc_ntree:deleaf0(Tree0),
|
||||||
|
IsntS = fun(Tk) -> isnt_str(S, Tk) end,
|
||||||
|
case lists:splitwith(IsntS, Kids0) of
|
||||||
|
% found
|
||||||
|
% input:
|
||||||
|
% *s Root0
|
||||||
|
% |
|
||||||
|
% +-- .l Foo
|
||||||
|
% +-- .l "=>"
|
||||||
|
% +-- .l Bar
|
||||||
|
% output:
|
||||||
|
% *s "=>"
|
||||||
|
% |
|
||||||
|
% +-- *s Root0 -- .l Foo
|
||||||
|
% +-- *s Root0 -- .l Bar
|
||||||
|
{LHS1, [Tk0 | RHS1]} ->
|
||||||
|
Root1 = Root0,
|
||||||
|
LTree1 = gsc_ntree:releaf0(Root1, LHS1),
|
||||||
|
RTree1 = rerootl_tkstr(S, gsc_ntree:releaf0(Root1, RHS1)),
|
||||||
|
NewRoot0 = {op, Tk0},
|
||||||
|
NewKids0 = [LTree1, RTree1],
|
||||||
|
NewTree = gsc_ntree:releaf0(NewRoot0, NewKids0),
|
||||||
|
NewTree;
|
||||||
|
% not found, nothing to do
|
||||||
|
{Kids0, []} ->
|
||||||
|
Tree0
|
||||||
|
end.
|
||||||
|
|
||||||
|
|
||||||
|
%reroot_mapsto(Tree0 = #ns{val = Root0}) ->
|
||||||
|
% Kids0 = gsc_ntree:deleaf0(Tree0),
|
||||||
|
% IsntMapsto = fun(DL) -> isnt_str("=>", Tk) end,
|
||||||
|
% case lists:splitwith(IsntMapsto, Kids0) of
|
||||||
|
% % found
|
||||||
|
% {LHS1, [Tk0 | RHS1]} ->
|
||||||
|
% Root1 = Root0,
|
||||||
|
% LTree1 = gsc_ntree:releaf0(Root1, LHS1),
|
||||||
|
% RTree1 = reroot_mapsto(gsc_ntree:releaf0(Root1, RHS1)),
|
||||||
|
% NewRoot0 = {op, Tk0},
|
||||||
|
% NewKids0 = [LTree1, RTree1],
|
||||||
|
% NewTree = gsc_ntree:releaf0(NewRoot0, NewKids0),
|
||||||
|
% NewTree;
|
||||||
|
% % nothing to do
|
||||||
|
% {Kids0, []} ->
|
||||||
|
% Tree0
|
||||||
|
% end.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
isnt_str(X, Y) ->
|
||||||
|
not is_str(X, Y).
|
||||||
|
|
||||||
|
is_str(S, #tk{str = S}) -> true;
|
||||||
|
is_str(_, _) -> false.
|
||||||
+112
-77
@@ -6,6 +6,32 @@
|
|||||||
|
|
||||||
-include("$gsc_include/gsc.hrl").
|
-include("$gsc_include/gsc.hrl").
|
||||||
|
|
||||||
|
% records copypasta for now
|
||||||
|
-record(ns, {meta :: any(), kids :: list(any())}).
|
||||||
|
|
||||||
|
-type ntree(X, Y) :: gsc_ntree:ntree(X, Y).
|
||||||
|
-type nforest(X, Y) :: gsc_nforest:nforest(X, Y).
|
||||||
|
|
||||||
|
-type nt(X, Y) :: gsc_ntree:ntree(X, Y).
|
||||||
|
-type nf(X, Y) :: gsc_nforest:nforest(X, Y).
|
||||||
|
|
||||||
|
|
||||||
|
% just parsing type expressions right now, so only need
|
||||||
|
% to worry about round parens
|
||||||
|
%
|
||||||
|
% none is to indicate general-purpose grouping, for
|
||||||
|
% e.g. LHS/RHS of an op
|
||||||
|
-type syntax_meta()
|
||||||
|
:: none
|
||||||
|
| {op, tk()}
|
||||||
|
| {parens, Open :: tk(), Close :: tk()}
|
||||||
|
.
|
||||||
|
|
||||||
|
-type ast() :: ntree(StemMeta :: syntax_meta(),
|
||||||
|
LeafType :: tk()).
|
||||||
|
-type asf() :: nforest(syntax_meta(), tk()).
|
||||||
|
-type asts() :: asf().
|
||||||
|
|
||||||
|
|
||||||
main() ->
|
main() ->
|
||||||
x00(),
|
x00(),
|
||||||
@@ -17,93 +43,102 @@ x00() ->
|
|||||||
io:format(" SrcStr = ~p~n", [x00_src()]),
|
io:format(" SrcStr = ~p~n", [x00_src()]),
|
||||||
io:format(" Tokens = ~p~n", [x00_tks()]),
|
io:format(" Tokens = ~p~n", [x00_tks()]),
|
||||||
io:format(" Signal = ~p~n", [x00_sgl()]),
|
io:format(" Signal = ~p~n", [x00_sgl()]),
|
||||||
io:format(" Tree0 = ~p~n", [x00_tree0()]),
|
io:format(" Forest = ~p~n", [x00_fst()]),
|
||||||
ok.
|
ok.
|
||||||
|
|
||||||
% sample type expr, tokens, signal
|
% sample type expr, tokens, signal
|
||||||
x00_src() -> "foo => bar * baz".
|
x00_src() -> "(foo => (bar) * baz)".
|
||||||
x00_tks() -> gsc:unsafe_tokens_from_string(x00_src()).
|
x00_tks() -> gsc:unsafe_tokens_from_string(x00_src()).
|
||||||
x00_sgl() -> gsc:filter_signal(x00_tks()).
|
x00_sgl() -> gsc:filter_signal(x00_tks()).
|
||||||
x00_tree0() -> mktree(x00_sgl()).
|
x00_fst() -> parse(x00_sgl()).
|
||||||
|
|
||||||
% records copypasta for now
|
|
||||||
-record(ns, {val :: any(), kids :: list(any())}).
|
|
||||||
-record(nl, {val :: any()}).
|
|
||||||
|
|
||||||
-type ntree(X, Y) :: gsc_ntree:ntree(X, Y).
|
|
||||||
-type ntree() :: gsc_ntree:ntree().
|
|
||||||
|
|
||||||
-type ast_stem_t() :: vtokens
|
|
||||||
| {op, tk()}
|
|
||||||
.
|
|
||||||
|
|
||||||
-type ast() :: ntree(ast_stem_t(), tk()).
|
|
||||||
|
|
||||||
|
|
||||||
-spec mktree(Signal) -> Tree when
|
-spec parse(Signal) -> ASF when
|
||||||
Signal :: gsc:signal(),
|
Signal :: [tk()],
|
||||||
Tree :: gsc_ntree:ntree().
|
ASF :: asf().
|
||||||
|
|
||||||
% @doc make into a tree
|
parse(Signal) ->
|
||||||
mktree(Sig) ->
|
% key insight here is our signal is already a
|
||||||
Tree0 = gsc_ntree:nstem(vtokens, Sig),
|
% forest, assuming the leaf type is `tk()`.
|
||||||
Tree1 = rerootl_tkstr("=>", Tree0),
|
%
|
||||||
Tree2 = rerootl_tkstr("*", Tree1),
|
% our parser is a sequence of forest-to-forest
|
||||||
Tree2.
|
% transformers.
|
||||||
|
%
|
||||||
|
% at the end we should end up with just one tree (i
|
||||||
|
% think)?
|
||||||
|
F0 = Signal,
|
||||||
|
F1 = f2f_parens(F0),
|
||||||
|
%F2 = f2f_op("=>", F1),
|
||||||
|
Result = F1,
|
||||||
|
Result.
|
||||||
|
|
||||||
|
|
||||||
rerootl_tkstr(S, Tree0 = #ns{val = Root0}) ->
|
%f2f_op(OpStr, Fst) ->
|
||||||
Kids0 = gsc_ntree:deleaf0(Tree0),
|
% case f2f_op(OpStr, [], none, Fst) of
|
||||||
IsntS = fun(Tk) -> isnt_str(S, Tk) end,
|
% % never saw it, no change
|
||||||
case lists:splitwith(IsntS, Kids0) of
|
% ident -> Fst;
|
||||||
% found
|
%
|
||||||
% input:
|
%
|
||||||
% *s Root0
|
%% never saw the op
|
||||||
% |
|
%f2f_op(_, _, none, []) ->
|
||||||
% +-- .l Foo
|
% ident;
|
||||||
% +-- .l "=>"
|
%% see op
|
||||||
% +-- .l Bar
|
%f2f_op(OpStr, LhsStk, none, [OpTk = #tk{str = OpStr} | Rest]) ->
|
||||||
% output:
|
% Lhf = lists:reverse(LhsStk),
|
||||||
% *s "=>"
|
% Rhf = f2f_op(OpStr, Rest),
|
||||||
% |
|
% Lht = #ns{meta = none, kids = Lhf},
|
||||||
% +-- *s Root0 -- .l Foo
|
% Rht = #ns{meta = none, kids = Rhf},
|
||||||
% +-- *s Root0 -- .l Bar
|
% Result =
|
||||||
{LHS1, [Tk0 | RHS1]} ->
|
|
||||||
Root1 = Root0,
|
|
||||||
LTree1 = gsc_ntree:releaf0(Root1, LHS1),
|
|
||||||
RTree1 = rerootl_tkstr(S, gsc_ntree:releaf0(Root1, RHS1)),
|
|
||||||
NewRoot0 = {op, Tk0},
|
|
||||||
NewKids0 = [LTree1, RTree1],
|
|
||||||
NewTree = gsc_ntree:releaf0(NewRoot0, NewKids0),
|
|
||||||
NewTree;
|
|
||||||
% not found, nothing to do
|
|
||||||
{Kids0, []} ->
|
|
||||||
Tree0
|
|
||||||
end.
|
|
||||||
|
|
||||||
|
|
||||||
%reroot_mapsto(Tree0 = #ns{val = Root0}) ->
|
-spec f2f_parens(Forest) -> NewForest when
|
||||||
% Kids0 = gsc_ntree:deleaf0(Tree0),
|
Forest :: asts(),
|
||||||
% IsntMapsto = fun(DL) -> isnt_str("=>", Tk) end,
|
NewForest :: Forest.
|
||||||
% case lists:splitwith(IsntMapsto, Kids0) of
|
% @doc
|
||||||
% % found
|
% recursive parens decomposition
|
||||||
% {LHS1, [Tk0 | RHS1]} ->
|
%
|
||||||
% Root1 = Root0,
|
% the input here is the flat list of tokens. here we
|
||||||
% LTree1 = gsc_ntree:releaf0(Root1, LHS1),
|
% basically replace the string of tokens between `(`
|
||||||
% RTree1 = reroot_mapsto(gsc_ntree:releaf0(Root1, RHS1)),
|
% and `)` with a single tree
|
||||||
% NewRoot0 = {op, Tk0},
|
%
|
||||||
% NewKids0 = [LTree1, RTree1],
|
% interesting quirk is that this doesn't error on too
|
||||||
% NewTree = gsc_ntree:releaf0(NewRoot0, NewKids0),
|
% many close parens, only too many open parens
|
||||||
% NewTree;
|
|
||||||
% % nothing to do
|
f2f_parens(Fst) ->
|
||||||
% {Kids0, []} ->
|
f2f_parens([], Fst).
|
||||||
% Tree0
|
|
||||||
% end.
|
% done
|
||||||
|
f2f_parens(Stk, []) ->
|
||||||
|
lists:reverse(Stk);
|
||||||
|
% crawl down the forest and scan for open parens
|
||||||
|
% open paren, we descend
|
||||||
|
f2f_parens(Stk, [#tk{str = "("} = TkOpen | Rest0]) ->
|
||||||
|
InitMeta = {parens, TkOpen, none},
|
||||||
|
{slurp, PStem, Rest1} = slurp_pstem(InitMeta, [], Rest0),
|
||||||
|
NewStk = [PStem | Stk],
|
||||||
|
f2f_parens(NewStk, Rest1);
|
||||||
|
% something else, we continue
|
||||||
|
f2f_parens(Stk, [Tree | Rest]) ->
|
||||||
|
f2f_parens([Tree | Stk], Rest).
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
isnt_str(X, Y) ->
|
% ran out of tokens before close paren
|
||||||
not is_str(X, Y).
|
slurp_pstem({parens, TkOpen, none}, Stk, []) ->
|
||||||
|
error({no_close_for, TkOpen, Stk});
|
||||||
is_str(S, #tk{str = S}) -> true;
|
% hit close paren, we done
|
||||||
is_str(_, _) -> false.
|
slurp_pstem({parens, TkOpen, none}, Stk, [TkClose = #tk{str = ")"} | Rest]) ->
|
||||||
|
FinalMeta = {parens, TkOpen, TkClose},
|
||||||
|
Midsection = lists:reverse(Stk),
|
||||||
|
FinalTree = #ns{meta = FinalMeta,
|
||||||
|
kids = Midsection},
|
||||||
|
{slurp, FinalTree, Rest};
|
||||||
|
% hit open paren, we recurse
|
||||||
|
slurp_pstem(AccMeta, Stk, [TkOpen_II = #tk{str = "("} | Rest0]) ->
|
||||||
|
InitMeta_II = {parens, TkOpen_II, none},
|
||||||
|
{slurp, PStem_II, Rest1} = slurp_pstem(InitMeta_II, [], Rest0),
|
||||||
|
NewStk = [PStem_II | Stk],
|
||||||
|
slurp_pstem(AccMeta, NewStk, Rest1);
|
||||||
|
% hit something else, we move along
|
||||||
|
slurp_pstem(AccMeta, Stk, [Tree | Rest]) ->
|
||||||
|
slurp_pstem(AccMeta, [Tree | Stk], Rest).
|
||||||
|
|||||||
+62
-98
@@ -1,15 +1,15 @@
|
|||||||
-module(gsc_ntree).
|
-module(gsc_ntree).
|
||||||
|
|
||||||
-export_type([
|
-export_type([
|
||||||
ntree/2,
|
ntree/2, ntree/0,
|
||||||
ntree/0
|
nforest/2, nforest/0,
|
||||||
|
nt/2, nt/0,
|
||||||
|
nf/2, nf/0
|
||||||
]).
|
]).
|
||||||
|
|
||||||
-export([
|
-export([
|
||||||
nstem/2,
|
nstem/2, meta/1, kids/1,
|
||||||
flatten/1,
|
flatten_tree/1, flatten_forest/1
|
||||||
deleaf0/1,
|
|
||||||
releaf0/2
|
|
||||||
]).
|
]).
|
||||||
|
|
||||||
|
|
||||||
@@ -19,16 +19,32 @@
|
|||||||
%% API: types
|
%% API: types
|
||||||
%%=====================================================
|
%%=====================================================
|
||||||
|
|
||||||
-record(ns, {val :: any(), kids :: list(any())}).
|
% @doc stem record
|
||||||
-record(nl, {val :: any()}).
|
-record(ns, {meta :: any(),
|
||||||
|
kids :: list(any())}).
|
||||||
|
|
||||||
%% @doc ntree(S, L) is a "node tree" (meaning stems
|
% @doc `ntree(S, L)' is a "node tree" (meaning stems
|
||||||
%% have values and children)
|
% have values and children)
|
||||||
-type ntree(S, L)
|
%
|
||||||
:: #ns{val :: S, kids :: [ntree(S, L)]}
|
% for the purposes of the compiler, the key observation
|
||||||
| #nl{val :: L}.
|
% 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
|
-spec nstem(Root, Forest) -> 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
|
|
||||||
Root :: S,
|
Root :: S,
|
||||||
Keeids :: [L | ntree(S, L)],
|
Forest :: nforest(S, L),
|
||||||
Rooted :: ntree(S, L),
|
Tree :: ntree(S, L),
|
||||||
S :: any(),
|
S :: any(),
|
||||||
L :: any().
|
L :: any().
|
||||||
|
|
||||||
% @doc notional inverse of `deleaf0/1'
|
nstem(Root, List) ->
|
||||||
%
|
{ns, Root, List}.
|
||||||
% Note that this does **NOT** double-wrap leafs in the
|
|
||||||
% input
|
|
||||||
releaf0(Root, Ks) ->
|
|
||||||
#ns{val = Root,
|
|
||||||
kids = lists:map(fun rl0/1, Ks)}.
|
|
||||||
|
|
||||||
rl0(X = #ns{}) -> X;
|
|
||||||
rl0(X = #nl{}) -> X;
|
meta(#ns{meta = M}) -> M.
|
||||||
rl0(X) -> {nl, X}.
|
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].
|
||||||
|
|||||||
Reference in New Issue
Block a user