% @doc % helper functions for grabbing collections of tokens % off the token stream % % generally assume no whitespace/comment tokens in % input stream -module(sfc_token_chunks). -export_type([ ]). -export([ barf/2 ]). % $sfc_include is so c() works from sfp eshell -include("$sfc_include/sfc.hrl"). %------------------------------------------ % Types %------------------------------------------ -type chunk_shape() :: block | block_item | {block_item, Level :: pos_integer()} . % FIXME -type choke_reason() :: any(). -spec barf(ChunkShape, SigTokens) -> Perhaps when ChunkShape :: chunk_shape(), SigTokens :: Tokens, Perhaps :: {barf, Taken, Rest} | {choke, Reason}, Taken :: Tokens, Rest :: Tokens, Reason :: choke_reason(), Tokens :: [sfc_token()]. % @doc % slurp/barf terminology comes from paredit mode in % emacs % % slurp ~= accepting input % barf ~= separating input % % slurp: (foo bar) baz ~> (foo bar baz) % barf : (foo bar baz) ~> foo (bar baz) barf(_, []) -> {barf, [], []}; barf(block, [H = #sfc_token{pos = {_, BlkCol}} | T]) -> Take = fun(#sfc_token{pos = {_, TkCol}}) -> BlkCol =< TkCol end, {A, B} = tw(Take, T), {barf, [H | A], B}; barf(block_item, [H = #sfc_token{pos = {_, BlkCol}} | T]) -> Take = fun(#sfc_token{pos = {_, TkCol}}) -> BlkCol < TkCol end, {A, B} = tw(Take, T), {barf, [H | A], B}. barf({block_item, Level}, Tokens = [#sfc_token{pos = {_, StartLevel}} | _]) -> case Level =:= StartLevel of false -> {barf, [], Tokens}; true -> barf(block_item, Tokens) end. barf(_, _) -> {choke, #sfc_err_nyi{}}. % takewhile but returns {Taken, Rest} tw(Pred, List) -> tw(Pred, [], List). tw(Pred, Stk, [X | Xs]) -> case Pred(X) of true -> tw(Pred, [X | Stk], Xs); false -> {lists:reverse(Stk), [X | Xs]} end; tw(_, Stk, []) -> {lists:reverse(Stk), []}. %-spec consume_delist_round(Tokens) -> Result % when Tokens :: [sfc_token()], % Result :: {ok, {Delist, NewTokens}} % | {error, sfc_err()}, % Delist :: Tokens, % NewTokens :: Tokens. % %consume_delist_round(Tokens = [#sfc_token{type = punct, string = "("} | _]) -> % consume_delist(Tokens); %consume_delist_round(Tokens) -> % {ok, {[], Tokens}}. % % % %-spec consume_delist_square(Tokens) -> Result % when Tokens :: [sfc_token()], % Result :: {ok, {Delist, NewTokens}} % | {error, sfc_err()}, % Delist :: Tokens, % NewTokens :: Tokens. % %consume_delist_square(Tokens = [#sfc_token{type = punct, string = "["} | _]) -> % consume_delist(Tokens); %consume_delist_square(Tokens) -> % {ok, {[], Tokens}}. % % % %-spec consume_delist_curly(Tokens) -> Result % when Tokens :: [sfc_token()], % Result :: {ok, {Delist, NewTokens}} % | {error, sfc_err()}, % Delist :: Tokens, % NewTokens :: Tokens. % %consume_delist_curly(Tokens = [#sfc_token{type = punct, string = "{"} | _]) -> % consume_delist(Tokens); %consume_delist_curly(Tokens) -> % {ok, {[], Tokens}}. % % % %-spec consume_delist(Tokens) -> Result % when Tokens :: [sfc_token()], % Result :: {ok, {Delist, NewTokens}} % | {error, sfc_err()}, % Delist :: Tokens, % NewTokens :: Tokens. % %consume_delist([Head | Tail]) -> % case Head of % #sfc_token{type = punct, string = OpenStr = "("} -> % CloseStr = ")", % delist_verb(1, {OpenStr, CloseStr}, [Head], Tail); % #sfc_token{type = punct, string = OpenStr = "["} -> % CloseStr = "]", % delist_verb(1, {OpenStr, CloseStr}, [Head], Tail); % #sfc_token{type = punct, string = OpenStr = "{"} -> % CloseStr = "}", % delist_verb(1, {OpenStr, CloseStr}, [Head], Tail); % _ -> % {ok, {[], [Head | Tail]}} % end; %consume_delist([]) -> % {ok, {[], []}}. % % %% happy path: have a token and we are in the depth of the list %delist_verb(Depth, {OpenStr, CloseStr}, Stack, [Tok | Rest]) when Depth >= 1 -> % NewDepth = % case Tok of % % open token -> increase depth % #sfc_token{type = punct, string = Open} when Open =:= OpenStr -> % Depth + 1; % % close token -> decrease depth % #sfc_token{type = punct, string = Close} when Close =:= CloseStr -> % Depth - 1; % _ -> % Depth % end, % delist_verb(NewDepth, {OpenStr, CloseStr}, [Tok | Stack], Rest); %% we've hit end of available input before a close token %delist_verb(Depth, {OpenStr, _CloseStr}, _Stack, []) when Depth >= 1 -> % {error, #sfc_err{atom = unclosed_delimiter, % string = io_lib:format("unclosed open delimiter: ~p", [OpenStr])}}; %% we've popped back out of the list, we're done %delist_verb(_Depth = 0, {_OpenStr, _CloseStr}, Stack, Tokens) -> % {ok, {lists:reverse(Stack), Tokens}}.