-module(gsc_test_ntree). -export([ main/0 ]). -include("$gsc_include/gsc.hrl"). % 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() :: {op, tk()} | op_arg | {parens, Open :: tk(), Close :: tk()} . -type ast() :: ntree(syntax_meta(), tk()). -type asf() :: nforest(syntax_meta(), tk()). -type asts() :: asf(). main() -> x00(), ok. % x00 = example00 x00() -> io:format("Example 00:~n", []), io:format(" SrcStr = ~p~n", [x00_src()]), io:format(" Tokens = ~p~n", [x00_tks()]), io:format(" Signal = ~p~n", [x00_sgl()]), io:format(" Forest = ~p~n", [x00_fst()]), ok. % sample type expr, tokens, signal x00_src() -> "(foo => (bar) * baz)". x00_tks() -> gsc:unsafe_tokens_from_string(x00_src()). x00_sgl() -> gsc:filter_signal(x00_tks()). x00_fst() -> parse(x00_sgl()). -spec parse(Signal) -> ASF when Signal :: [tk()], ASF :: asf(). parse(Signal) -> % key insight here is our signal is already a % forest, assuming the leaf type is `tk()`. % % our parser is a sequence of forest-to-forest % transformers. % % at the end we should end up with just one tree (i % think)? F0 = Signal, F1 = f2f_parens(F0), F2 = f2f_op("=>", F1), F3 = f2f_op("*", F2), Result = F2, Result. f2f_op(OpStr, Fst) -> f2f_op(OpStr, [], Fst). % never saw the op f2f_op(_opstr, Stk, []) -> lists:reverse(Stk); % see op f2f_op(OpStr, LhsStk, [#tk{str = OpStr} = OpTk | Rest]) -> Lhf = lists:reverse(LhsStk), Rhf = f2f_op(OpStr, Rest), Lht = #ns{meta = op_arg, kids = Lhf}, Rht = #ns{meta = op_arg, kids = Rhf}, ResultT = #ns{meta = {op, OpTk}, kids = [Lht, Rht]}, ResultF = [ResultT], ResultF; % see stem, descend f2f_op(OpStr, LhsStk, [Ns = #ns{kids = NsKids} | Rest]) -> NewNsKids = f2f_op(OpStr, NsKids), NewNs = Ns#ns{kids = NewNsKids}, NewStk = [NewNs | LhsStk], f2f_op(OpStr, NewStk, Rest); % see leaf, just add f2f_op(OpStr, Stk, [L | Rest]) -> f2f_op(OpStr, [L | Stk], Rest). -spec f2f_parens(Forest) -> NewForest when Forest :: asts(), NewForest :: Forest. % @doc % recursive parens decomposition % % the input here is the flat list of tokens. here we % basically replace the string of tokens between `(` % and `)` with a single tree % % interesting quirk is that this doesn't error on too % many close parens, only too many open parens f2f_parens(Fst) -> f2f_parens([], Fst). % 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). % ran out of tokens before close paren slurp_pstem({parens, TkOpen, none}, Stk, []) -> error({no_close_for, TkOpen, Stk}); % hit close paren, we done 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).