179 lines
5.1 KiB
Erlang
179 lines
5.1 KiB
Erlang
% @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}}.
|